http

http报文结构

我们可以通过burpsuite来抓取http请求,然后根据请求编写处理程序。

  • 这是申请登录的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /login HTTP/1.1		//请求行
/* 请求头部
Host: localhost:1316
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Origin: http://localhost:1316
Connection: close
Referer: http://localhost:1316/login
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
*/
username=+%E7%BD%97&password=123456 //请求体 输入:(空格)罗 123456
  • 这是请求图片的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /picture HTTP/1.1		//请求行
/* 请求头部
Host: localhost:1316
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://localhost:1316/login
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
*/
  • 这是返回的http请求
1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Connection: close
Content-type: text/html
Content-length: 3347

<!DOCTYPE html>
<html lang="en">
...

MYSQL头文件常用函数

函数或结构体 描述
MYSQL_FIELD 用于表示结果集中的列信息,包括列名、列类型、列大小等。
MYSQL_RES 表示 SQL 查询结果集的结构体。执行查询后,结果会被存储在这个结构体中。
mysql_query() 用于执行一个 SQL 查询的函数。它接受一个 MySQL 连接句柄和一个 SQL 查询字符串作为参数。
mysql_free_result() 用于释放由 mysql_store_result() 返回的结果集占用的内存的函数。
mysql_store_result() 用于获取并存储 mysql_query() 执行的查询结果的函数。它返回一个 MYSQL_RES 类型的结果集。
mysql_num_fields() 用于获取结果集中的列数的函数。
mysql_fetch_fields() 用于获取结果集中的列信息数组的函数,数组的每个元素都是一个 MYSQL_FIELD 结构体。
mysql_fetch_row() 用于从结果集中获取一行数据的函数。它返回一个指向字符串数组的指针,数组中的每个元素都对应结果集中的一列。
mysql_free_result() 用于释放结果集占用的内存的函数。
1
2
3
4
5
struct sockaddr_in {
short int sin_family; // 通常设置为 AF_INET,表示 IPv4 地址
u_short int sin_port; // 端口号,以网络字节顺序存储
struct in_addr sin_addr; // IP 地址
};

HttpRequest.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H

#include <unordered_map>
#include <unordered_set>
#include <string>
#include <regex>

#include <errno.h>
#include <mysql/mysql.h>

#include "../log/log.h"
#include "../buffer/buffer.h"
#include "../pool/sqlconnpool.h"
#include "../pool/sqlconnRAII.h"


class HttpRequest{
public:
enum class PARSE_STATE{
REQUEST_LINE,
HEADERS,
BODY,
FINISH,
};
enum class HTTP_CODE{
NO_REQUEST = 0,
GET_REQUEST,
BAD_REQUEST,
NO_RESOURSEA,
FORBIDDENT_REQUEST,
FILE_REQUEST,
INTERNAL_ERROR,
CLOSED_CONNECTION,
};


HttpRequest();
~HttpRequest();

void Init();
bool parse(Buffer& buff);

std::string path() const;
std::string& path();
std::string method() const;
std::string version() const;
std::string GetPost(const std::string& key) const;
std::string GetPost(const char* key) const;

bool IsKeepAlive() const;

/*
TODO
void HttpConn::ParseFormData() {}
void HttpConn::ParseJson() {}
*/
private:
bool ParesRequestLine_(const std::string& line);
void ParseHeader_(const std::string& line);
void ParseBody_(const std::string& line);

void ParsePath_();
void ParsePost_();
void ParseFromUrlencoded_();

static bool UserVerify(const std::string& name, const std::string& pwd, bool isLogin);

PARSE_STATE state_;
std::string method_, path_, version_, body_;
std::unordered_map<std::string, std::string> header_;
std::unordered_map<std::string, std::string> post_;

static const std::unordered_set<std::string> DeFALUT_HTML;
static const std::unordered_map<std::string, int> DEFALUT_HTML_TAG;
static int ConverHex(char ch);

};
#endif

HttpRequest.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275


#include "httprequest.h"

HttpRequest::HttpRequest() { Init(); }

HttpRequest::~HttpRequest() = default;

void HttpRequest::Init() {
method_ = path_ = version_ = body_ = "";
state_ = PARSE_STATE::REQUEST_LINE;
header_.clear();
post_.clear();
}

bool HttpRequest::parse(Buffer &buff) {
const char CRLF[] = "\r\n";
if (buff.ReadableBytes() <= 0)
return false;
while (buff.ReadableBytes() && state_ != PARSE_STATE::FINISH) {
const char *lineEnd =
std::search(buff.Peek(), buff.BeginWriteConst(), CRLF, CRLF + 2);
std::string line(buff.Peek(), lineEnd);
switch (state_) {
case PARSE_STATE::REQUEST_LINE:
if (!ParesRequestLine_(line))
return false;
ParsePath_();
break;
case PARSE_STATE::HEADERS:
ParseHeader_(line);
if (buff.ReadableBytes() <= 2)
state_ = PARSE_STATE::FINISH;
//若只有CRLF,则没有BODY
break;

case PARSE_STATE::BODY:
ParseBody_(line);
break;

default:
break;
}
if (lineEnd == buff.BeginWriteConst())
break;
buff.RetrieveUntil(lineEnd + 2); //记得+2
}
LOG_DEBUG("[%s], [%s], [%s]", method_.c_str(), path_.c_str(),
version_.c_str());
return true;
}

std::string HttpRequest::path() const { return path_; }
std::string &HttpRequest::path() { return path_; }
std::string HttpRequest::method() const { return method_; }
std::string HttpRequest::version() const { return version_; }
std::string HttpRequest::GetPost(const std::string &key) const {
assert(key != "");
if (post_.count(key) == 1) //找的到
return post_.find(key)->second;
return "";
}
std::string HttpRequest::GetPost(const char *key) const {
assert(key != nullptr);
if (post_.count(key) == 1) {
return post_.find(key)->second;
}
return "";
}

bool HttpRequest::IsKeepAlive() const {
if (header_.count("Connection") == 1)
return header_.find("Connection")->second == "keep-alive" &&
version_ == "1.1";
return false;
}

/*
TODO
void HttpConn::ParseFormData() {}
void HttpConn::ParseJson() {}
*/

bool HttpRequest::ParesRequestLine_(const std::string &line) {
std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
std::smatch subSmatch;
if (std::regex_match(line, subSmatch, patten)) {
//注意 subSmatch[0];表示匹配的整个字符串
method_ = subSmatch[1];
path_ = subSmatch[2];
version_ = subSmatch[3];
state_ = PARSE_STATE::HEADERS;
return true;
}
LOG_ERROR("RequestLine Error");
return false;
}

void HttpRequest::ParseHeader_(const std::string &line) {
std::regex pattern("^([^:]*): ?(.*)$");
std::smatch subSmatch;
if (std::regex_match(line, subSmatch, pattern)) {
header_[subSmatch[1]] = subSmatch[2];
} else {
state_ = PARSE_STATE::BODY;
}
}

void HttpRequest::ParseBody_(const std::string &line) {
body_ = line;
ParsePost_();
state_ = PARSE_STATE::FINISH;
LOG_DEBUG("Body:%s, len:%d", line.c_str(), line.size());
}

void HttpRequest::ParsePath_() {
if (path_ == "/") {
path_ = "/index.html";
} else {
for (auto &item : DeFALUT_HTML) {
if (item == path_) {
path_ += ".html";
break;
}
}
}
}
void HttpRequest::ParsePost_() {
if (method_ == "POST" &&
header_["Content-Type"] == "application/x-www-form-urlencoded") {
ParseFromUrlencoded_();
if (DEFALUT_HTML_TAG.count(path_)) {
int tag = DEFALUT_HTML_TAG.find(path_)->second;
if (tag == 0 || tag == 1) {
bool isLogin = (tag == 1);
if (UserVerify(post_["username"], post_["password"], isLogin)) {
path_ = "/welcome.html";
} else {
path_ = "/error.html";
}
}
}
}
}
//&、=、+、%等,这些字符在URL编码中用于表示特殊意义
void HttpRequest::ParseFromUrlencoded_() {
if (body_.size() == 0) {
return;
}

std::string key, value;
int num = 0;
int n = body_.size();
int i = 0, j = 0; //循环和unordered_map
for (; i < n; i++) {
switch (body_[i]) {
case '=':
key = body_.substr(j, i - j);
j = i + 1;
break;
case '+':
body_[i] = ' ';
break;
case '%':
num = ConverHex(body_[i + 1]) * 16 + ConverHex(body_[i + 2]);
body_[i + 1] = num % 10 + '0';
body_[i + 2] = num / 10 + '0';
i += 2;
break;
case '&':
value = body_.substr(j, i - j);
j = i + 1;
post_[key] = value;
LOG_DEBUG("%s = %s", key.c_str(), value.c_str());
LOG_DEBUG(body_.c_str());
break;
default:
break;
}
}
assert(j < i);
//处理最后一个键值对,因为最后不会有&
if (post_.count(key) == 0 && j < i) {
value = body_.substr(j, i - j);
post_[key] = value;
}
}

bool HttpRequest::UserVerify(const std::string &name, const std::string &pwd,
bool isLogin) {
if (name == "" || pwd == "")
return false;
LOG_INFO("Verify name:%s pwd:%s", name.c_str(), pwd.c_str());
MYSQL *sql;

SqlConnRAII(&sql, SqlConnPool::Instance());
assert(sql);

bool flag = false;
size_t j = 0;
char order[256] = {0};

MYSQL_FIELD *fileds = nullptr;
MYSQL_RES *res = nullptr;

if (!isLogin) {
flag = true;
}

snprintf(order, sizeof(order),
"select username, password from user WHERE username = '%s' LIMIT 1",
name.c_str());
LOG_DEBUG("%s", order);

if (mysql_query(sql, order)) //非0成功
{
mysql_free_result(res);

}
res = mysql_store_result(sql);
j = mysql_num_fields(res);
fileds = mysql_fetch_fields(res);

while (MYSQL_ROW row = mysql_fetch_row(res)) { //遍历
LOG_DEBUG("MYSQL ROW: %s %s", row[0], row[1]);
std::string password(row[1]);
if (isLogin) {
if (password == pwd) {
flag = true;
} else {
flag = false;
LOG_DEBUG("pwd error!");
}
} else {
flag = false;
LOG_DEBUG("user used!");
}
}

mysql_free_result(res);

if (!isLogin && flag == true) {
LOG_DEBUG("regirster!");
bzero(order, 256);
snprintf(order, 256,
"INSERT INTO user(username, password) VALUES('%s','%s')",
name.c_str(), pwd.c_str());
LOG_DEBUG("%s", order);
if (mysql_query(sql, order)) {
LOG_DEBUG("Insert error!");
flag = false;
}
flag = true;
}
SqlConnPool::Instance()->FreeConn(sql);
LOG_DEBUG("UserVerify success!!");
return flag;
}

const std::unordered_set<std::string> HttpRequest::DeFALUT_HTML{
"/index", "/register", "/login", "/welcome", "video", "/picture",
};

const std::unordered_map<std::string, int> HttpRequest::DEFALUT_HTML_TAG{
{{"/register.html", 0}, {"/login.html", 1}},
};

int HttpRequest::ConverHex(char ch) {
if (ch >= 'A' && ch <= 'F')
ch = ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f')
ch = ch - 'a' + 10;
return ch;
}

HttpResponse.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56


#ifndef HTTP_RESPONSE_H
#define HTTP_RESPONSE_H

#include <cstddef> //包括常用类型和宏的定义,比如 size_t 和 NULL。
#include <string>
#include <unordered_map>
#include <fcntl.h> //于提供文件控制操作
#include <unistd.h> //POSIX操作系统的API
#include <sys/stat.h> //文件状态和属性操作的结构和函数原型
#include <sys/mman.h> //提供了内存映射文件的操作原型

#include "../buffer/buffer.h"
#include "../log/log.h"

class HttpResponse{
public:
HttpResponse();
~HttpResponse();

void Init(const std::string& srcDir, std::string& path, bool isKeepAlice = false, int code = -1);
void MakeResponse(Buffer& buff);
void UnmapFile();
char* File();
size_t FileLen() const;
void ErrorContent(Buffer& buff, std::string message);
int Code() const;

private:
void AddStateLine_(Buffer &buff);
void AddHeader_(Buffer &buff);
void AddContent_(Buffer&buff);

void ErrorHtml_();
std::string GetFileType_();

private:
int code_;
bool isKeepAlive_;

std::string path_;
std::string srcDir_;

char* mmFile_;
struct stat mmFileStat_;

static const std::unordered_map<std::string, std::string> SUFFIX_TYPE;
static const std::unordered_map<int, std::string> CODE_STATUS;
static const std::unordered_map<int , std::string> CODE_PATH;
};


#endif


HttpResponse.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "httpresponse.h"
#include <fcntl.h>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>

const std::unordered_map<std::string, std::string> HttpResponse::SUFFIX_TYPE = {
{".html", "text/html"},
{".xml", "text/xml"},
{".xhtml", "application/xhtml+xml"},
{".txt", "text/plain"},
{".rtf", "application/rtf"},
{".pdf", "application/pdf"},
{".word", "application/nsword"},
{".png", "image/png"},
{".gif", "image/gif"},
{".jpg", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".au", "audio/basic"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".avi", "video/x-msvideo"},
{".gz", "application/x-gzip"},
{".tar", "application/x-tar"},
{".css", "text/css "},
{".js", "text/javascript "},
};

const std::unordered_map<int, std::string> HttpResponse::CODE_STATUS = {
{200, "OK"},
{400, "Bad Request"},
{403, "Forbidden"},
{404, "Not Found"},
};

const std::unordered_map<int, std::string> HttpResponse::CODE_PATH = {
{400, "/400.html"},
{403, "/403.html"},
{404, "/404.html"},
};

HttpResponse::HttpResponse() {
code_ = -1;
path_ = srcDir_ = "";
isKeepAlive_ = false;
mmFile_ = nullptr;
mmFileStat_ = {0};
}

HttpResponse::~HttpResponse() { UnmapFile(); }

void HttpResponse::Init(const std::string &srcDir, std::string &path,
bool isKeepAlive, int code) {
assert(srcDir != "");
if (mmFile_) UnmapFile();
code_ = code;
isKeepAlive_ = isKeepAlive;
path_ = path;
srcDir_ = srcDir;
mmFile_ = nullptr;
mmFileStat_ = { 0 };
}

void HttpResponse::MakeResponse(Buffer &buff) {
if (stat((srcDir_ + path_).data(), &mmFileStat_) < 0 ||
S_ISDIR(mmFileStat_.st_mode))
code_ = 404;
else if (!(mmFileStat_.st_mode & S_IROTH)) {
code_ = 403;
} else if (code_ == -1) {
code_ = 200;
}
ErrorHtml_();
AddStateLine_(buff);
AddHeader_(buff);
AddContent_(buff);
}

void HttpResponse::UnmapFile() {
if (mmFile_) {
munmap(mmFile_, mmFileStat_.st_size);
mmFile_ = nullptr; //避免悬垂指针
}
}

char *HttpResponse::File() { return mmFile_; }
size_t HttpResponse::FileLen() const { return mmFileStat_.st_size; }

void HttpResponse::ErrorContent(Buffer &buff, std::string message) {
std::string body;
std::string status;
body += "<html><title>Error</title>";
body += "<body bgcolor=\"ffffff\">";
if (CODE_STATUS.count(code_) == 1) {
status = CODE_STATUS.find(code_)->second;
} else {
status = "Bad Request";
}
body += std::to_string(code_) + " : " + status + "\n";
body += "<p>" + message + "</p>";
body += "<hr><em>TinyWebServer</em></body></html>";

buff.Append("Content-length: " + std::to_string(body.size()) + "\r\n\r\n");
buff.Append(body);
}

int HttpResponse::Code() const { return code_; }

void HttpResponse::AddStateLine_(Buffer &buff) {
std::string status;
if (CODE_STATUS.count(code_) == 1) {
status = CODE_STATUS.find(code_)->second;
} else {
code_ = 400;
status = CODE_STATUS.find(code_)->second;
}
buff.Append("HTTP/1.1 " + std::to_string(code_) + " " + status +
"\r\n"); //别忘了分隔符
}

void HttpResponse::AddHeader_(Buffer &buff) {
buff.Append("Connection: ");
if (isKeepAlive_) {
buff.Append("keep-alive\r\n");
buff.Append("keep-alive: max=6, timeout=120\r\n");
} else {
buff.Append("close\r\n");
}
buff.Append("Content-type: " + GetFileType_() + "\r\n");
}

void HttpResponse::AddContent_(Buffer &buff) {
int srcFd = open((srcDir_ + path_).data(), O_RDONLY);
if (srcFd < 0) {
ErrorContent(buff, "File NotFound!");
return;
}
LOG_DEBUG("file path %s", (srcDir_ + path_).data());
int *mmRet = (int *)mmap(nullptr, mmFileStat_.st_size, PROT_READ, MAP_PRIVATE,
srcFd, 0);
if (*mmRet == -1) {
ErrorContent(buff, "File NotFound!");
return;
}
mmFile_ = (char *)mmRet;
close(srcFd);
buff.Append("Content-length: " + std::to_string(mmFileStat_.st_size) +
"\r\n\r\n");
}

void HttpResponse::ErrorHtml_() {
if (CODE_PATH.count(code_) == 1) {
path_ = CODE_PATH.find(code_)->second;
stat((srcDir_ + path_).data(), &mmFileStat_);
}
}

std::string HttpResponse::GetFileType_() {
//判断文件类型
std::string::size_type idx = path_.find_last_of('.');
if (idx == std::string::npos)
return "text/plain";
std::string suffix = path_.substr(idx, path_.length() - idx);

if (SUFFIX_TYPE.count(suffix) == 1)
return SUFFIX_TYPE.find(suffix)->second;

return "text/plain";
}

HttpConn.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#ifndef HTTP_CONN_H
#define HTTP_CONN_H

#include <sys/types.h>
#include <sys/uio.h>
#include <arpa/inet.h> //ip地址协议等
#include <stdlib.h>
#include <errno.h>
#include "../log/log.h"
#include "../pool/sqlconnRAII.h"
#include "../buffer/buffer.h"
#include "httprequest.h"
#include "httpresponse.h"

class HttpConn {
public:
HttpConn();

~HttpConn();

void init(int sockFd, const sockaddr_in& addr);

ssize_t read(int* saveErrno);

ssize_t write(int* saveErrno);

void Close();

int GetFd() const;

int GetPort() const;

const char* GetIP() const;

sockaddr_in GetAddr() const;

bool process();

int ToWriteBytes() ;
bool IsKeepAlive() const ;

static bool isET;
static const char* srcDir;
static std::atomic<int> userCount;

private:

int fd_;
struct sockaddr_in addr_;

bool isClose_;

int iovCnt_;
struct iovec iov_[2];

Buffer readBuff_; // 读缓冲区
Buffer writeBuff_; // 写缓冲区

HttpRequest request_;
HttpResponse response_;
};


#endif

HttpConn.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "httpconn.h"


// static bool isET;
// static const char *srcDir;
// static std::atomic<int> userCount;


const char* HttpConn::srcDir;
std::atomic<int> HttpConn::userCount;
bool HttpConn::isET;

HttpConn::HttpConn()
{
fd_ = -1;
addr_ = { 0 };
isClose_ = true;
}

HttpConn::~HttpConn()
{
Close();
}

void HttpConn::init(int sockFd, const sockaddr_in &addr)
{
assert(sockFd > 0);
userCount++;
writeBuff_.RetrieveAll();
readBuff_.RetrieveAll();
fd_ = sockFd;
addr_ = addr;
isClose_ = false;
LOG_INFO("Client[%d](%s:%d) in, userCount:%d", fd_, GetIP(), GetPort(), (int)userCount);
}

ssize_t HttpConn::read(int *saveErrno)
{
ssize_t len = -1;
do{
len = readBuff_.ReadFd(fd_, saveErrno);
if(len <= 0) break;
}
while(isET);
return len;
}

ssize_t HttpConn::write(int *saveErrno)
{
ssize_t len = -1;
do{
len = writev(fd_,iov_,iovCnt_);
if(len <= 0 )
{ *saveErrno = errno;
break;
}
if(iov_[0].iov_len + iov_[1].iov_len == 0) break;

else if(iov_[0].iov_len < static_cast<size_t>(len))
{
iov_[1].iov_base = (uint8_t*) iov_[1].iov_base + (len - iov_[1].iov_len);
iov_[1].iov_len -= (len - iov_[0].iov_len);
if(iov_[0].iov_len)
{
writeBuff_.RetrieveAll();
iov_[0].iov_len = 0;
}
}else
{
iov_[0].iov_base = (uint8_t*) iov_[0].iov_base + len ;
iov_[0].iov_len -= len;
writeBuff_.Retrieve(len);
}


}while(isET || ToWriteBytes() > 10240);
return len;
}

void HttpConn::Close()
{
response_.UnmapFile();
if(isClose_ == false)
{
isClose_ = true;
close(fd_);
userCount --;
LOG_INFO("Client[%d](%s:%d) quit, UserCount:%d", fd_, GetIP(), GetPort(), (int)userCount);
}
}

int HttpConn::GetFd() const
{
return fd_;
}

int HttpConn::GetPort() const
{
return addr_.sin_port;
}



const char* HttpConn::GetIP() const
{
return inet_ntoa(addr_.sin_addr);
}

sockaddr_in HttpConn::GetAddr() const
{
return addr_;
}

bool HttpConn::process()
{
request_.Init();
if(readBuff_.ReadableBytes() <= 0) return false;
else if (request_.parse(readBuff_))
{

response_.Init(srcDir, request_.path(), request_.IsKeepAlive(), 200 );
}
else {
response_.Init(srcDir, request_.path(), false, 400);
}
response_.MakeResponse(writeBuff_);

iov_[0].iov_base = const_cast<char *>(writeBuff_.Peek());
iov_[0].iov_len = writeBuff_.ReadableBytes();
iovCnt_ = 1;
if(response_.FileLen() > 0 && response_.File())
{
iov_[1].iov_base = response_.File();
iov_[1].iov_len = response_.FileLen();
iovCnt_ = 2;
}
LOG_DEBUG("filesize:%d, %d to %d", response_.FileLen() , iovCnt_, ToWriteBytes());
return true;
}

int HttpConn::ToWriteBytes()
{ return iov_[0].iov_len + iov_[1].iov_len; }

bool HttpConn::IsKeepAlive() const { return request_.IsKeepAlive(); }