2 #include <sys/socket.h>
3 #include <netinet/in.h>
6 #include <openssl/ssl.h>
17 Link::Client::Client(Link::Request
* request
) {
18 this->request
= request
;
19 if (this->request
->GetProtocol() != "https" && this->request
->GetProtocol() != "http") {
20 std::cout
<< "Invalid protocol: " << this->request
->GetProtocol() << std::endl
;
24 Link::Request
* Link::Client::SetRequest(Link::Request
* request
) {
25 this->request
= request
;
29 Link::Request
* Link::Client::GetRequest() {
33 Link::Response
* Link::Client::GetResponse() {
34 return this->response
;
37 int Link::Client::Write(const void* buf
, size_t count
) {
38 if (this->request
->GetProtocol() == "https") return SSL_write((SSL
*)ssl
, buf
, count
);
39 else return write(sock
, buf
, count
);
42 int Link::Client::Read(void* buf
, size_t count
) {
43 if (this->request
->GetProtocol() == "https") return SSL_read((SSL
*)ssl
, buf
, count
);
44 else return read(sock
, buf
, count
);
47 bool Link::Client::getChunkSize(int& remaining
, std::string
& body
) {
48 std::string chunkSizeStr
= "";
50 while (chunkSizeStr
.find("\r\n") == std::string::npos
) {
51 int bytes
= Read(buffer
, 1);
55 if (chunkSizeStr
== "" || chunkSizeStr
== "\r" || chunkSizeStr
== "\n") {
63 chunkSizeStr
+= buffer
[0];
70 remaining
= std::stoi(chunkSizeStr
, 0, 16);
71 if (remaining
== 0) return true;
75 Link::Response
* Link::Client::Send() {
77 if (this->request
->GetProtocol() == "https") {
78 ctx
= SSL_CTX_new(SSLv23_client_method());
80 struct sockaddr_in addr
;
81 bzero(&addr
, sizeof(addr
));
82 addr
.sin_family
= AF_INET
;
83 if (this->request
->GetPort()==0) addr
.sin_port
= htons(this->request
->GetProtocol()=="https"?443:80); // TODO: Parse port from URL
84 else addr
.sin_port
= htons(this->request
->GetPort());
85 struct hostent
* ipv4
= gethostbyname(this->request
->GetDomain().c_str());
86 addr
.sin_addr
.s_addr
= inet_addr(inet_ntoa(*(struct in_addr
*)ipv4
->h_addr_list
[0]));
88 socklen_t socklen
= sizeof(addr
);
90 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
91 if (connect(sock
, (struct sockaddr
*)&addr
, socklen
) < 0) std::cout
<< "Connection failed" << std::endl
;
93 if (this->request
->GetProtocol() == "https") {
95 SSLeay_add_ssl_algorithms();
96 SSL_load_error_strings();
97 // SSL_CTX_set_cipher_list(ctx, "TLS_AES_256_GCM_SHA384");
98 SSL_CTX_set_max_proto_version(ctx
, TLS1_3_VERSION
);
100 int newsock
= SSL_get_fd(ssl
);
101 SSL_set_fd(ssl
, sock
);
102 SSL_set_tlsext_host_name(ssl
, this->request
->GetDomain().c_str());
103 int error
= SSL_connect(ssl
);
104 if (error
< 0) std::cout
<< "SSL connection failed" << std::endl
;
107 std::string request
= this->request
->GetMethod() + " " + this->request
->GetPath() + " HTTP/1.1\r\n"; // TODO: Add HTTP version variable
108 this->request
->SetHeader("Connection", "close");
109 this->request
->SetHeader("Accept", "*/*");
110 this->request
->SetHeader("Accept-Encoding", "gzip, deflate");
111 this->request
->SetHeader("Accept-Language", "en-US,en;q=0.9");
112 this->request
->SetHeader("User-Agent", "Link/2.0.0");
113 request
+= this->request
->GetRawHeaders();
115 request
+= this->request
->GetBody();
117 int status
= Write(request
.c_str(), strlen(request
.c_str()));
118 if (status
< 0) std::cout
<< "Write failed: " << status
<< std::endl
;
120 int flags
= fcntl(sock
, F_GETFL
, 0);
121 fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
);
125 std::string response
;
127 while (response
.find("\r\n\r\n") == std::string::npos
) {
128 int bytes
= Read(buffer
, 1);
129 if (bytes
> 0) response
+= std::string(buffer
, bytes
);
132 std::string body
, headers
;
134 std::string lower
= response
;
135 std::transform(lower
.begin(), lower
.end(), lower
.begin(), ::tolower
);
137 if (lower
.find("transfer-encoding: chunked") != std::string::npos
) {
139 * Finally figured out how to do this
140 * This should not be a problem anymore
143 // Check for the first chunk size
144 headers
= response
.substr(0, response
.find("\r\n\r\n"));
145 body
= response
.substr(response
.find("\r\n\r\n") + 4, response
.find("\r\n0\r\n\r\n") - response
.find("\r\n\r\n") - 4);
146 if (body
.substr(0, body
.find("\r\n")).length() > 0) {
147 // We have a chunk size
149 * This check is no longer needed, but I'm leaving it here for now
152 remaining
= std::stoi(body
.substr(0, body
.find("\r\n")), 0, 16);
154 // Need to find the next chunk size
155 getChunkSize(remaining
, body
);
157 while (remaining
> 0) {
158 int bytes
= Read(buffer
, remaining
>1024?1024:remaining
);
160 body
+= std::string(buffer
, bytes
);
165 while (!getChunkSize(remaining
, body
)) {
166 while (remaining
> 0) {
167 int bytes
= Read(buffer
, remaining
>1024?1024:remaining
);
169 body
+= std::string(buffer
, bytes
);
175 remaining
= std::stoi(lower
.substr(lower
.find("content-length: ") + 16, lower
.find("\r\n", lower
.find("content-length: ") + 16) - lower
.find("content-length: ") - 16));
176 remaining
-=response
.substr(response
.find("\r\n\r\n") + 4).length();
178 while (remaining
> 0) {
179 int bytes
= Read(buffer
, 1024);
181 response
+= std::string(buffer
, bytes
);
186 body
= response
.substr(response
.find("\r\n\r\n") + 4);
187 headers
= response
.substr(0, response
.find("\r\n\r\n"));
189 Response
* res
= new Response(headers
, body
);
191 if (this->request
->GetProtocol() == "https") {