request.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #pragma once
  2. #include <nall/decode/url.hpp>
  3. #include <nall/encode/url.hpp>
  4. #include <nall/http/message.hpp>
  5. namespace nall::HTTP {
  6. struct Request : Message {
  7. using type = Request;
  8. enum class RequestType : uint { None, Head, Get, Post };
  9. explicit operator bool() const { return requestType() != RequestType::None; }
  10. inline auto head(const function<bool (const uint8_t* data, uint size)>& callback) const -> bool override;
  11. inline auto setHead() -> bool override;
  12. inline auto body(const function<bool (const uint8_t* data, uint size)>& callback) const -> bool override;
  13. inline auto setBody() -> bool override;
  14. auto ipv4() const -> bool { return _ipv6 == false; }
  15. auto ipv6() const -> bool { return _ipv6 == true; }
  16. auto ip() const -> string { return _ip; }
  17. auto requestType() const -> RequestType { return _requestType; }
  18. auto setRequestType(RequestType value) -> void { _requestType = value; }
  19. auto path() const -> string { return _path; }
  20. auto setPath(const string& value) -> void { _path = value; }
  21. Variables cookie;
  22. Variables get;
  23. Variables post;
  24. //private:
  25. bool _ipv6 = false;
  26. string _ip;
  27. RequestType _requestType = RequestType::None;
  28. string _path;
  29. };
  30. auto Request::head(const function<bool (const uint8_t*, uint)>& callback) const -> bool {
  31. if(!callback) return false;
  32. string output;
  33. string request = path();
  34. if(get.size()) {
  35. request.append("?");
  36. for(auto& variable : get) {
  37. request.append(Encode::URL(variable.name()), "=", Encode::URL(variable.value()), "&");
  38. }
  39. request.trimRight("&", 1L);
  40. }
  41. switch(requestType()) {
  42. case RequestType::Head: output.append("HEAD ", request, " HTTP/1.1\r\n"); break;
  43. case RequestType::Get : output.append("GET ", request, " HTTP/1.1\r\n"); break;
  44. case RequestType::Post: output.append("POST ", request, " HTTP/1.1\r\n"); break;
  45. default: return false;
  46. }
  47. for(auto& variable : header) {
  48. output.append(variable.name(), ": ", variable.value(), "\r\n");
  49. }
  50. output.append("\r\n");
  51. return callback(output.data<uint8_t>(), output.size());
  52. }
  53. auto Request::setHead() -> bool {
  54. auto headers = _head.split("\n");
  55. string request = headers.takeLeft().trimRight("\r", 1L);
  56. string requestHost;
  57. if(request.iendsWith(" HTTP/1.0")) request.itrimRight(" HTTP/1.0", 1L);
  58. else if(request.iendsWith(" HTTP/1.1")) request.itrimRight(" HTTP/1.1", 1L);
  59. else return false;
  60. if(request.ibeginsWith("HEAD ")) request.itrimLeft("HEAD ", 1L), setRequestType(RequestType::Head);
  61. else if(request.ibeginsWith("GET " )) request.itrimLeft("GET ", 1L), setRequestType(RequestType::Get );
  62. else if(request.ibeginsWith("POST ")) request.itrimLeft("POST ", 1L), setRequestType(RequestType::Post);
  63. else return false;
  64. //decode absolute URIs
  65. request.strip().itrimLeft("http://", 1L);
  66. if(!request.beginsWith("/")) {
  67. auto components = request.split("/", 1L);
  68. requestHost = components(0);
  69. request = {"/", components(1)};
  70. }
  71. auto components = request.split("?", 1L);
  72. setPath(components(0));
  73. if(auto queryString = components(1)) {
  74. for(auto& block : queryString.split("&")) {
  75. auto p = block.split("=", 1L);
  76. auto name = Decode::URL(p(0));
  77. auto value = Decode::URL(p(1));
  78. if(name) get.append(name, value);
  79. }
  80. }
  81. for(auto& header : headers) {
  82. if(header.beginsWith(" ") || header.beginsWith("\t")) continue;
  83. auto part = header.split(":", 1L).strip();
  84. if(!part[0] || part.size() != 2) continue;
  85. this->header.append(part[0], part[1]);
  86. if(part[0].iequals("Cookie")) {
  87. for(auto& block : part[1].split(";")) {
  88. auto p = block.split("=", 1L).strip();
  89. auto name = p(0);
  90. auto value = p(1).trim("\"", "\"", 1L);
  91. if(name) cookie.append(name, value);
  92. }
  93. }
  94. }
  95. if(requestHost) header.assign("Host", requestHost); //request URI overrides host header
  96. return true;
  97. }
  98. auto Request::body(const function<bool (const uint8_t*, uint)>& callback) const -> bool {
  99. if(!callback) return false;
  100. if(_body) {
  101. return callback(_body.data<uint8_t>(), _body.size());
  102. }
  103. return true;
  104. }
  105. auto Request::setBody() -> bool {
  106. if(requestType() == RequestType::Post) {
  107. auto contentType = header["Content-Type"].value();
  108. if(contentType.iequals("application/x-www-form-urlencoded")) {
  109. for(auto& block : _body.split("&")) {
  110. auto p = block.trimRight("\r").split("=", 1L);
  111. auto name = Decode::URL(p(0));
  112. auto value = Decode::URL(p(1));
  113. if(name) post.append(name, value);
  114. }
  115. } else if(contentType.imatch("multipart/form-data; boundary=?*")) {
  116. auto boundary = contentType.itrimLeft("multipart/form-data; boundary=", 1L).trim("\"", "\"", 1L);
  117. auto blocks = _body.split({"--", boundary}, 1024L); //limit blocks to prevent memory exhaustion
  118. for(auto& block : blocks) block.trim("\r\n", "\r\n", 1L);
  119. if(blocks.size() < 2 || (blocks.takeLeft(), !blocks.takeRight().beginsWith("--"))) return false;
  120. for(auto& block : blocks) {
  121. string name;
  122. string filename;
  123. string contentType;
  124. auto segments = block.split("\r\n\r\n", 1L);
  125. for(auto& segment : segments(0).split("\r\n")) {
  126. auto statement = segment.split(":", 1L);
  127. if(statement(0).ibeginsWith("Content-Disposition")) {
  128. for(auto& component : statement(1).split(";")) {
  129. auto part = component.split("=", 1L).strip();
  130. if(part(0).iequals("name")) {
  131. name = part(1).trim("\"", "\"", 1L);
  132. } else if(part(0).iequals("filename")) {
  133. filename = part(1).trim("\"", "\"", 1L);
  134. }
  135. }
  136. } else if(statement(0).ibeginsWith("Content-Type")) {
  137. contentType = statement(1).strip();
  138. }
  139. }
  140. if(name) {
  141. post.append(name, segments(1));
  142. post.append({name, ".filename"}, filename);
  143. post.append({name, ".content-type"}, contentType);
  144. }
  145. }
  146. }
  147. }
  148. return true;
  149. }
  150. }