Photon 1.0.0
Loading...
Searching...
No Matches
IHTTPProtocol.hpp
Go to the documentation of this file.
1/*
2 * =====================================================================
3 *
4 * Photon
5 * Copyright Amlal EL Mahrouss, all rights reserved.
6 *
7 * =====================================================================
8 */
9
10#pragma once
11
12#include <core/Core.hpp>
13#include <core/Socket.hpp>
14
15// OpenSSL headers.
16#include <cstddef>
17#include <openssl/err.h>
18#include <openssl/ssl.h>
19#include <string>
20
21#define PHOTON_HTTP_VER 1.1
22
23#define PHOTON_USE_HTTPS (443)
24#define PHOTON_USE_HTTP (80)
25#define PHOTON_HTTP_AGENT "Photon/1.0.0 (Photon) Photon/1.0.0 Version/1.0.0"
26
27namespace Photon::HTTP
28{
30
31 class MIMEFactory;
32 class IHTTPHelper;
33 class HTTPWriter;
34 class HTTPError;
35
36 class MIMEFactory final
37 {
38 public:
39 struct MIME final
40 {
41 std::string t_name;
42 std::string t_mime;
43 };
44
45 public:
46 MIMEFactory() = default;
47 ~MIMEFactory() = default;
48
49 MIMEFactory& operator=(const MIMEFactory&) = default;
50 MIMEFactory(const MIMEFactory&) = default;
51
52 public:
54 {
55 if (!name || !strchr(name, '.'))
56 return {.t_name = "N/A", .t_mime = "*/*"};
57
58 std::string extension = strchr(name, '.');
59
60 if (strstr(extension.c_str(), ".png"))
61 return {.t_name = "PNG Image", .t_mime = "image/png"};
62 else if (strstr(extension.c_str(), ".html"))
63 return {.t_name = "HTML Document", .t_mime = "text/html"};
64 else if (strstr(extension.c_str(), ".bmp"))
65 return {.t_name = "BMP Image", .t_mime = "image/bmp"};
66 else if (strstr(extension.c_str(), ".webp"))
67 return {.t_name = "WEBP Image", .t_mime = "image/webp"};
68 else if (strstr(extension.c_str(), ".exe"))
69 return {.t_name = "Microsoft Portable Executable", .t_mime = "application/vnd.microsoft.executable"};
70 else if (strstr(extension.c_str(), ".exec"))
71 return {.t_name = "NeKernel Preferred Executable Format", .t_mime = "application/vnd.ne.executable"};
72 else if (strstr(extension.c_str(), ".jpg"))
73 return {.t_name = "JPEG Image", .t_mime = "image/jpeg"};
74 else if (strstr(extension.c_str(), ".zip"))
75 return {.t_name = "PKZIP Archive", .t_mime = "application/zip"};
76 else if (strstr(extension.c_str(), ".svg"))
77 return {.t_name = "SVG Document", .t_mime = "image/svg+xml"};
78
79 return {.t_name = "N/A", .t_mime = "*/y"};
80 }
81 };
82
83 namespace HTTP
84 {
85 class HTTPSocket final
86 {
87 struct sockaddr_in m_Addr
88 {
89 0
90 };
91
92 std::string m_Dns{""};
94
95 friend HTTPWriter;
96
97 public:
98 HTTPSocket() = default;
99 ~HTTPSocket() = default;
100
101 HTTPSocket& operator=(const HTTPSocket&) = default;
102 HTTPSocket(const HTTPSocket&) = default;
103 };
104
105 enum class RequestType : uint8_t
106 {
107 GET,
108 POST,
109 PUT,
110 DEL,
111 };
112
118
119 } // namespace HTTP
120
131
132 class HTTPError final : public std::runtime_error
133 {
134 public:
135 explicit HTTPError(const std::uint16_t what) : std::runtime_error("HTTP_RESULT_")
136 {
137 }
138
139 ~HTTPError() override = default; // let the ABI define that.
140
141 HTTPError& operator=(const HTTPError&) = default;
142 HTTPError(const HTTPError&) = default;
143
144 Int32 error() const
145 {
146 return mError;
147 }
148
149 std::string as_string() noexcept
150 {
151 std::string base = this->what();
152 base += std::to_string(mError);
153
154 return base;
155 }
156
157 private:
158 int mError{200};
159 };
160
161 inline std::string PHOTON_HTTP_GET = "GET";
162 inline std::string PHOTON_HTTP_POST = "POST";
163 inline std::string PHOTON_HTTP_PUT = "PUT";
164 inline std::string PHOTON_HTTP_DELETE = "DELETE";
165
167 {
168 public:
169 static std::string form_request(const std::string path,
170 const std::string host,
171 const std::string request_type,
172 const size_t length = 0,
173 std::vector<std::pair<std::string, std::string>> headers = {},
174 String data = "")
175 {
176 if (path.empty() || host.empty())
177 throw BrowserError("NO_HOST_PROVIDED");
178
179 PHOTON_INFO("Forming packet...");
180
181 std::string request = request_type;
182
183 request += " ";
184 request += path;
185 request += " HTTP/1.1\r\n";
186 request += "Host: ";
187 request += host;
188 request += "\r\n";
189 request += "User-Agent: ";
190 request += PHOTON_HTTP_AGENT;
191 request += "\r\n";
192
193 MIMEFactory factory;
194 auto mime_struct = factory(const_cast<char*>(path.data()));
195
196 if (length > 0 && request_type != PHOTON_HTTP_GET)
197 {
198 request += "Content-Length: ";
199 request += std::to_string(length);
200 request += "\r\n";
201 }
202
203 request += "Accept: ";
204 request += mime_struct.t_mime;
205 request += "\r\n";
206
207 for (const auto& header : headers)
208 {
209 request += header.first;
210 request += ": ";
211 request += header.second;
212 request += "\r\n";
213 }
214
215 request += "Connection: keep-alive\r\n";
216 request += "\r\n";
217
218 if (data.size() > 0 && request_type == PHOTON_HTTP_POST)
219 {
220 request += data;
221 }
222
223 PHOTON_INFO("HTTP packet has been created...");
224
225 return request;
226 }
227
228 static String get_field_value(const std::string& http, const std::string& name)
229 {
230 if (http.empty())
231 return "";
232
233 if (name.empty())
234 throw BrowserError("BAD_FIELD_NAME_TYPE");
235
236 auto value = http.substr(http.find(name));
237 value.erase(value.find("\r\n"));
238
239 PHOTON_INFO("Field's value has been fetched...");
240
241 return value;
242 }
243
244 static bool has_field(const std::string& http, const std::string& rest)
245 {
246 if (http.empty())
247 return false;
248
249 if (rest.empty())
250 throw BrowserError("BAD_RESTRICT_TYPE");
251
252 return http.find(rest) != std::string::npos;
253 }
254
255 template <size_t Base> static long content_length(const std::string& http)
256 {
257 String value = IHTTPHelper::get_field_value(http, "Content-Length:");
258
259 if (value.empty())
260 return IHTTPHelper::bad_pos;
261
262 std::string final;
263
264 for (size_t first = 0; first < value.size(); ++first)
265 {
266 if (value[first] == ' ')
267 continue;
268
269 if (value[first] >= '0' && value[first] <= '9')
270 {
271 final += value[first];
272 }
273 }
274
275 PHOTON_INFO(final);
276
277 return std::atol(final.c_str());
278 }
279
280 static const int bad_pos = 0;
281 };
282
283 using HTTPSharedPtr = std::shared_ptr<HTTP::HTTPSocket>;
284
285 class HTTPWriter final
286 {
287 public:
288 explicit HTTPWriter(bool use_https)
289 {
290 if (!use_https)
291 return;
292
294 m_Ssl = SSL_new(m_SslCtx);
295
296 // in case the SSL context fails
297 if (m_Ssl == nullptr)
298 {
299 fprintf(stderr, "SSL_new() failed\n");
300
301 throw BrowserError("Bad SSL context, SSL_new() failed!");
302 }
303
304 PHOTON_WARN("Init HTTPS SSL.");
305 }
306
307 ~HTTPWriter() = default;
308
309 public:
310 HTTPWriter& operator=(const HTTPWriter&) = default;
311 HTTPWriter(const HTTPWriter&) = default;
312
313 public:
314 HTTPSharedPtr create_and_connect(const std::string dns)
315 {
316 if (dns.empty())
318
319 PHOTON_INFO("Creating HTTPSharedPtr...");
320
321 HTTPSharedPtr sock = std::make_unique<HTTP::HTTPSocket>();
322
323 if (!sock)
325
326 PHOTON_INFO("Creating network socket...");
327
328 sock->m_Socket = ::PHOTON_SOCKET(AF_INET, SOCK_STREAM, 0);
329
330 if (sock->m_Socket == INVALID_SOCKET)
332
333 memset(&sock->m_Addr, 0, sizeof(struct sockaddr_in));
334
335 sock->m_Addr.sin_family = AF_INET;
336
337 sock->m_Addr.sin_addr.s_addr = inet_addr(dns.c_str());
338 sock->m_Addr.sin_port = htons(PHOTON_HTTP_PORT);
339
340 PHOTON_INFO("Connecting using socket...");
341
342 if (sock->m_Addr.sin_addr.s_addr == INADDR_NONE)
343 {
344 struct hostent* host = gethostbyname(dns.c_str());
345
346 if (!host)
347 {
348 PHOTON_CLOSE(sock->m_Socket);
349
350 PHOTON_INFO("Invalid hostname! returning nullptr...");
351 PHOTON_INFO(dns.c_str());
352
353 throw BrowserError("INVALID_DNS_PROBE_X2");
354 }
355
356 sock->m_Addr.sin_addr.s_addr = *((u_long*)host->h_addr);
357 }
358
359 sock->m_Dns = std::string{dns.data()};
360
361 int result =
362 ::connect(sock->m_Socket, reinterpret_cast<struct sockaddr*>(&sock->m_Addr), sizeof(sock->m_Addr));
363
364 if (result == SOCKET_ERROR)
365 {
366 perror("HTTP");
367 return nullptr;
368 }
369
371 {
372 SSL_set_fd(m_Ssl, sock->m_Socket);
373 auto status = SSL_connect(m_Ssl);
374
375 if (status <= 0)
376 {
377 return nullptr;
378 }
379
380 PHOTON_INFO(std::string("Connected with HTTPS encryption: ") + SSL_get_cipher(m_Ssl));
381
382 return sock;
383 }
384 else
385 {
386 PHOTON_INFO("Connected with HTTP.");
387 }
388
389 return sock;
390 }
391
392 bool close_socket() noexcept
393 {
394 if (m_Socket)
395 {
396 if (PHOTON_SHUTDOWN(m_Socket->m_Socket, SD_BOTH) == SOCKET_ERROR)
397 PHOTON_CLOSE(m_Socket->m_Socket);
398
399 if (m_Ssl)
400 {
401 SSL_free(m_Ssl);
402 SSL_CTX_free(m_SslCtx);
403 }
404
405 return true;
406 }
407
408 return false;
409 }
410
411 bool send_from_socket(HTTPSharedPtr& sock, const char* bytes, size_t len)
412 {
413 if (!sock || !len || !bytes)
414 return false;
415
417 {
418 return ::SSL_write(m_Ssl, bytes, len);
419 }
420 else
421 {
422 return ::send(sock->m_Socket, bytes, len, 0) > 0;
423 }
424 }
425
426 int64_t read_from_socket(HTTPSharedPtr& sock, char* bytes, int len)
427 {
428 if (!sock || !bytes || len < 1)
429 return -1;
430
432 {
433 auto ret = ::SSL_read(m_Ssl, bytes, len);
434
435 bytes[len - 1] = 0;
436
437 if (ret == -1)
438 return 0;
439
440 return ret;
441 }
442 else
443 {
444 auto ret = ::recv(sock->m_Socket, bytes, len, 0);
445 bytes[len - 1] = 0;
446
447 if (ret == -1)
448 return 0;
449
450 return ret;
451 }
452 }
453
454 private:
455 SSL_CTX* _init_ssl(void) noexcept
456 {
457 const SSL_METHOD* method = TLS_client_method(); /* Create new client-method instance */
458 SSL_CTX* ctx = SSL_CTX_new(method);
459
460 if (ctx == nullptr)
461 {
462 ERR_print_errors_fp(stderr);
463 return nullptr;
464 }
465
466 return ctx;
467 }
468
469 private:
471 SSL_CTX* m_SslCtx{nullptr};
472 SSL* m_Ssl{nullptr};
473 };
474} // namespace Photon::HTTP
#define PHOTON_WARN(...)
Definition Core.hpp:167
#define PHOTON_INFO(...)
Definition Core.hpp:166
#define PHOTON_USE_HTTPS
Definition IHTTPProtocol.hpp:23
#define PHOTON_HTTP_AGENT
Definition IHTTPProtocol.hpp:25
#define PHOTON_CLOSE
Definition Socket.hpp:38
#define PHOTON_SHUTDOWN
Definition Socket.hpp:39
#define SOCKET_ERROR
Definition Socket.hpp:27
#define PHOTON_SOCKET
Definition Socket.hpp:37
Definition Core.hpp:46
Definition IHTTPProtocol.hpp:86
HTTPSocket(const HTTPSocket &)=default
std::string m_Dns
Definition IHTTPProtocol.hpp:92
HTTPSocket & operator=(const HTTPSocket &)=default
friend HTTPWriter
Definition IHTTPProtocol.hpp:95
Network::CSocket m_Socket
Definition IHTTPProtocol.hpp:93
Definition IHTTPProtocol.hpp:133
HTTPError(const std::uint16_t what)
Definition IHTTPProtocol.hpp:135
HTTPError(const HTTPError &)=default
std::string as_string() noexcept
Definition IHTTPProtocol.hpp:149
int mError
Definition IHTTPProtocol.hpp:158
HTTPError & operator=(const HTTPError &)=default
~HTTPError() override=default
Int32 error() const
Definition IHTTPProtocol.hpp:144
Definition IHTTPProtocol.hpp:286
SSL * m_Ssl
Definition IHTTPProtocol.hpp:472
bool send_from_socket(HTTPSharedPtr &sock, const char *bytes, size_t len)
Definition IHTTPProtocol.hpp:411
HTTPSharedPtr m_Socket
Definition IHTTPProtocol.hpp:470
SSL_CTX * _init_ssl(void) noexcept
Definition IHTTPProtocol.hpp:455
HTTPWriter & operator=(const HTTPWriter &)=default
SSL_CTX * m_SslCtx
Definition IHTTPProtocol.hpp:471
bool close_socket() noexcept
Definition IHTTPProtocol.hpp:392
HTTPWriter(const HTTPWriter &)=default
int64_t read_from_socket(HTTPSharedPtr &sock, char *bytes, int len)
Definition IHTTPProtocol.hpp:426
HTTPSharedPtr create_and_connect(const std::string dns)
Definition IHTTPProtocol.hpp:314
HTTPWriter(bool use_https)
Definition IHTTPProtocol.hpp:288
Definition IHTTPProtocol.hpp:167
static bool has_field(const std::string &http, const std::string &rest)
Definition IHTTPProtocol.hpp:244
static std::string form_request(const std::string path, const std::string host, const std::string request_type, const size_t length=0, std::vector< std::pair< std::string, std::string > > headers={}, String data="")
Definition IHTTPProtocol.hpp:169
static String get_field_value(const std::string &http, const std::string &name)
Definition IHTTPProtocol.hpp:228
static long content_length(const std::string &http)
Definition IHTTPProtocol.hpp:255
Definition IHTTPProtocol.hpp:37
MIMEFactory & operator=(const MIMEFactory &)=default
MIMEFactory(const MIMEFactory &)=default
MIMEFactory::MIME operator()(char *name)
Definition IHTTPProtocol.hpp:53
Definition format.h:4610
Definition core.h:1598
#define SD_BOTH
Definition Config.hpp:40
#define INVALID_SOCKET
Definition Config.hpp:44
#define PHOTON_API
Definition Config.hpp:64
RequestType
Definition IHTTPProtocol.hpp:106
Definition IHTTPProtocol.hpp:28
int16_t PHOTON_HTTP_PORT
Definition IHTTPProtocol.hpp:29
std::string PHOTON_HTTP_DELETE
Definition IHTTPProtocol.hpp:164
std::shared_ptr< HTTP::HTTPSocket > HTTPSharedPtr
Definition IHTTPProtocol.hpp:283
std::string PHOTON_HTTP_GET
Definition IHTTPProtocol.hpp:161
std::string PHOTON_HTTP_POST
Definition IHTTPProtocol.hpp:162
HTTP_ERROR_LIST
Definition IHTTPProtocol.hpp:122
@ HTTP_NOT_FOUND
Definition IHTTPProtocol.hpp:125
@ HTTP_ERROR_COUNT
Definition IHTTPProtocol.hpp:129
@ HTTP_DNS_ERROR
Definition IHTTPProtocol.hpp:127
@ HTTP_BAD_GATEWAY
Definition IHTTPProtocol.hpp:124
@ HTTP_BAD_REQ
Definition IHTTPProtocol.hpp:126
@ HTTP_OK
Definition IHTTPProtocol.hpp:123
@ HTTP_INTERNAL_ERROR
Definition IHTTPProtocol.hpp:128
std::string PHOTON_HTTP_PUT
Definition IHTTPProtocol.hpp:163
uintptr_t CSocket
Definition Socket.hpp:67
std::string String
Definition Core.hpp:37
std::int32_t Int32
Definition Config.hpp:95
Definition uuid.h:926
auto fprintf(std::FILE *f, const S &fmt, const T &... args) -> int
Definition printf.h:760
Definition IHTTPProtocol.hpp:114
String Bytes
Definition IHTTPProtocol.hpp:116
RequestType Type
Definition IHTTPProtocol.hpp:115
Definition IHTTPProtocol.hpp:88
Definition IHTTPProtocol.hpp:40
std::string t_mime
Definition IHTTPProtocol.hpp:42
std::string t_name
Definition IHTTPProtocol.hpp:41
Definition format.h:1901
base
Definition tag_strings.h:7
header
Definition tag_strings.h:26