2 #include <sys/socket.h>
3 #include <netinet/in.h>
6 #include <openssl/ssl.h>
17 Link::Client::Client(Link::Request
* request
) {
19 this->request
= request
;
20 if (this->request
->GetProtocol() != "https" && this->request
->GetProtocol() != "http") {
21 std::cout
<< "Invalid protocol: " << this->request
->GetProtocol() << std::endl
;
25 Link::Request
* Link::Client::SetRequest(Link::Request
* request
) {
26 this->request
= request
;
30 Link::Request
* Link::Client::GetRequest() {
34 Link::Response
* Link::Client::GetResponse() {
35 return this->response
;
38 int Link::Client::Write(const void* buf
, size_t count
) {
39 if (this->request
->GetProtocol() == "https") return SSL_write((SSL
*)ssl
, buf
, count
);
40 else return send(sock
, buf
, count
, 0);
43 int Link::Client::Read(void* buf
, size_t count
) {
44 if (this->request
->GetProtocol() == "https") return SSL_read((SSL
*)ssl
, buf
, count
);
45 else return recv(sock
, buf
, count
, 0);
48 bool Link::Client::getChunkSize(int& remaining
, std::string
& body
) {
49 std::string chunkSizeStr
= "";
51 while (chunkSizeStr
.find("\r\n") == std::string::npos
) {
52 int bytes
= Read(buffer
, 1);
56 if (chunkSizeStr
== "" || chunkSizeStr
== "\r" || chunkSizeStr
== "\n") {
64 chunkSizeStr
+= buffer
[0];
71 remaining
= std::stoi(chunkSizeStr
, 0, 16);
72 if (remaining
== 0) return true;
76 Link::Response
* Link::Client::Send() {
79 if (this->request
->GetProtocol() == "https") {
80 ctx
= SSL_CTX_new(SSLv23_client_method());
82 struct sockaddr_in addr
;
83 bzero(&addr
, sizeof(addr
));
84 addr
.sin_family
= AF_INET
;
85 if (this->request
->GetPort()==0) addr
.sin_port
= htons(this->request
->GetProtocol()=="https"?443:80); // TODO: Parse port from URL
86 else addr
.sin_port
= htons(this->request
->GetPort());
87 struct hostent
* ipv4
= gethostbyname(this->request
->GetDomain().c_str());
88 addr
.sin_addr
.s_addr
= inet_addr(inet_ntoa(*(struct in_addr
*)ipv4
->h_addr_list
[0]));
90 socklen_t socklen
= sizeof(addr
);
92 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
93 if (connect(sock
, (struct sockaddr
*)&addr
, socklen
) < 0) {
98 if (this->request
->GetProtocol() == "https") {
100 SSLeay_add_ssl_algorithms();
101 SSL_load_error_strings();
102 // SSL_CTX_set_cipher_list(ctx, "TLS_AES_256_GCM_SHA384");
103 SSL_CTX_set_max_proto_version(ctx
, TLS1_3_VERSION
);
105 int newsock
= SSL_get_fd(ssl
);
106 SSL_set_fd(ssl
, sock
);
107 SSL_set_tlsext_host_name(ssl
, this->request
->GetDomain().c_str());
108 int error
= SSL_connect(ssl
);
115 std::string r
= this->request
->GetRawHeaders();
117 r
+= this->request
->GetBody();
119 int status
= Write(r
.c_str(), strlen(r
.c_str()));
125 int flags
= fcntl(sock
, F_GETFL
, 0);
126 fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
);
130 std::string response
;
132 while (response
.find("\r\n\r\n") == std::string::npos
) {
133 int bytes
= Read(buffer
, 1);
134 if (bytes
> 0) response
+= std::string(buffer
, bytes
);
137 std::string body
, headers
;
139 std::string lower
= response
;
140 std::transform(lower
.begin(), lower
.end(), lower
.begin(), ::tolower
);
143 if (lower
.find("transfer-encoding: chunked") != std::string::npos
) {
145 * Finally figured out how to do this
146 * This should not be a problem anymore
149 // Check for the first chunk size
150 headers
= response
.substr(0, response
.find("\r\n\r\n"));
151 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);
152 if (body
.substr(0, body
.find("\r\n")).length() > 0) {
153 // We have a chunk size
155 * This check is no longer needed, but I'm leaving it here for now
158 remaining
= std::stoi(body
.substr(0, body
.find("\r\n")), 0, 16);
160 // Need to find the next chunk size
161 getChunkSize(remaining
, body
);
163 while (remaining
> 0) {
164 int bytes
= Read(buffer
, remaining
>1024?1024:remaining
);
166 body
+= std::string(buffer
, bytes
);
171 while (!getChunkSize(remaining
, body
)) {
172 while (remaining
> 0) {
173 int bytes
= Read(buffer
, remaining
>1024?1024:remaining
);
175 body
+= std::string(buffer
, bytes
);
180 } else if (lower
.find("content-length: ") != std::string::npos
) {
181 remaining
= std::stoi(lower
.substr(lower
.find("content-length: ") + 16, lower
.find("\r\n", lower
.find("content-length: ") + 16) - lower
.find("content-length: ") - 16));
182 remaining
-=response
.substr(response
.find("\r\n\r\n") + 4).length();
184 while (remaining
> 0) {
185 int bytes
= Read(buffer
, 1024);
187 response
+= std::string(buffer
, bytes
);
192 body
= response
.substr(response
.find("\r\n\r\n") + 4);
193 headers
= response
.substr(0, response
.find("\r\n\r\n"));
194 } else headers
= response
;
195 Response
* res
= new Response(headers
, body
);
197 if (this->request
->GetProtocol() == "https") {