role.hpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #pragma once
  2. //Role: base class for Client and Server
  3. //provides shared functionality
  4. #include <nall/http/request.hpp>
  5. #include <nall/http/response.hpp>
  6. namespace nall::HTTP {
  7. struct Role {
  8. struct Settings {
  9. int connectionLimit = 1 * 1024; //server
  10. int headSizeLimit = 16 * 1024; //client, server
  11. int bodySizeLimit = 65536 * 1024; //client, server
  12. int chunkSize = 32 * 1024; //client, server
  13. int threadStackSize = 128 * 1024; //server
  14. int timeoutReceive = 15 * 1000; //server
  15. int timeoutSend = 15 * 1000; //server
  16. } settings;
  17. inline auto configure(const string& parameters) -> bool;
  18. inline auto download(int fd, Message& message) -> bool;
  19. inline auto upload(int fd, const Message& message) -> bool;
  20. };
  21. auto Role::configure(const string& parameters) -> bool {
  22. auto document = BML::unserialize(parameters);
  23. for(auto parameter : document) {
  24. auto name = parameter.name();
  25. auto value = parameter.integer();
  26. if(0);
  27. else if(name == "connectionLimit") settings.connectionLimit = value;
  28. else if(name == "headSizeLimit") settings.headSizeLimit = value;
  29. else if(name == "bodySizeLimit") settings.bodySizeLimit = value;
  30. else if(name == "chunkSize") settings.chunkSize = value;
  31. else if(name == "threadStackSize") settings.threadStackSize = value;
  32. else if(name == "timeoutReceive") settings.timeoutReceive = value;
  33. else if(name == "timeoutSend") settings.timeoutSend = value;
  34. }
  35. return true;
  36. }
  37. auto Role::download(int fd, Message& message) -> bool {
  38. auto& head = message._head;
  39. auto& body = message._body;
  40. string chunk;
  41. uint8_t packet[settings.chunkSize], *p = nullptr;
  42. head.reset(), head.reserve(4095);
  43. body.reset(), body.reserve(4095);
  44. bool headReceived = false;
  45. bool chunked = false;
  46. bool chunkReceived = false;
  47. bool chunkFooterReceived = true;
  48. int length = 0;
  49. int chunkLength = 0;
  50. int contentLength = 0;
  51. while(true) {
  52. if(auto limit = settings.headSizeLimit) if(head.size() >= limit) return false;
  53. if(auto limit = settings.bodySizeLimit) if(body.size() >= limit) return false;
  54. if(headReceived && !chunked && body.size() >= contentLength) {
  55. body.resize(contentLength);
  56. break;
  57. }
  58. if(length == 0) {
  59. length = recv(fd, packet, settings.chunkSize, MSG_NOSIGNAL);
  60. if(length <= 0) return false;
  61. p = packet;
  62. }
  63. if(!headReceived) {
  64. head.append((char)*p++);
  65. --length;
  66. if(head.endsWith("\r\n\r\n") || head.endsWith("\n\n")) {
  67. headReceived = true;
  68. if(!message.setHead()) return false;
  69. chunked = message.header["Transfer-Encoding"].value().iequals("chunked");
  70. contentLength = message.header["Content-Length"].value().natural();
  71. }
  72. continue;
  73. }
  74. if(chunked && !chunkReceived) {
  75. char n = *p++;
  76. --length;
  77. if(!chunkFooterReceived) {
  78. if(n == '\n') chunkFooterReceived = true;
  79. continue;
  80. }
  81. chunk.append(n);
  82. if(chunk.endsWith("\r\n") || chunk.endsWith("\n")) {
  83. chunkReceived = true;
  84. chunkLength = chunk.hex();
  85. if(chunkLength == 0) break;
  86. chunk.reset();
  87. }
  88. continue;
  89. }
  90. if(!chunked) {
  91. body.resize(body.size() + length);
  92. memory::copy(body.get() + body.size() - length, p, length);
  93. p += length;
  94. length = 0;
  95. } else {
  96. int transferLength = min(length, chunkLength);
  97. body.resize(body.size() + transferLength);
  98. memory::copy(body.get() + body.size() - transferLength, p, transferLength);
  99. p += transferLength;
  100. length -= transferLength;
  101. chunkLength -= transferLength;
  102. if(chunkLength == 0) {
  103. chunkReceived = false;
  104. chunkFooterReceived = false;
  105. }
  106. }
  107. }
  108. if(!message.setBody()) return false;
  109. return true;
  110. }
  111. auto Role::upload(int fd, const Message& message) -> bool {
  112. auto transfer = [&](const uint8_t* data, uint size) -> bool {
  113. while(size) {
  114. int length = send(fd, data, min(size, settings.chunkSize), MSG_NOSIGNAL);
  115. if(length < 0) return false;
  116. data += length;
  117. size -= length;
  118. }
  119. return true;
  120. };
  121. if(message.head([&](const uint8_t* data, uint size) -> bool { return transfer(data, size); })) {
  122. if(message.body([&](const uint8_t* data, uint size) -> bool { return transfer(data, size); })) {
  123. return true;
  124. }
  125. }
  126. return false;
  127. }
  128. }