22#include "http/HttpRequest.h"
29#include <unordered_map>
30#include "pico/stdlib.h"
40#ifdef PICO_HTTP_ENABLE_HTTP_CLIENT
44#define BUFFER_SIZE 256
102#ifdef PICO_HTTP_ENABLE_HTTP_CLIENT
105 client.sendRequest(*
this, response);
121 : method(reqMethod), path(reqPath)
126 size_t pos =
uri.find(
'?');
127 if (pos != std::string::npos)
162 TRACE(
"Parsing request data: %s\n", rawHeaders.c_str());
163 std::istringstream stream(rawHeaders);
164 std::string requestLine;
165 if (!std::getline(stream, requestLine)) {
166 printf(
"Failed to read request line\n");
170 std::istringstream lineStream(requestLine);
172 printf(
"Error parsing HTTP request method and path\n");
181 std::string requestText;
187 printf(
"[HttpRequest] Failed to receive header - usually Safari reusing socket it shouldn't\n");
191 requestText.append(buffer, received);
193 size_t headerEnd = requestText.find(
"\r\n\r\n");
197 std::string leftover = (bodyStart < requestText.size())
198 ? requestText.substr(bodyStart)
200 return std::make_pair(std::move(
headers), std::move(leftover));
204 printf(
"[HttpRequest] Headers exceeded %zu bytes, rejecting\n",
MAX_HEADER_BYTES);
211 size_t remaining = expectedLength -
body.size();
214 while (remaining > 0) {
215 size_t toRead = std::min(remaining,
sizeof(buffer));
218 printf(
"Error receiving body chunk\n");
221 size_t currentSize =
body.size();
223 TRACE(
"Body exceeds max length. Truncating.\n");
228 size_t toAppend = std::min(
static_cast<size_t>(received), allowed);
229 body.append(buffer, toAppend);
231 if (toAppend <
static_cast<size_t>(received)) {
232 TRACE(
"Body chunk truncated due to size limit.\n");
236 remaining -= received;
257 std::string
body =
"";
258 std::map<std::string, std::string>
headers;
260 std::string
path = {0};
267 const auto& [rawHeaders, initialBody] = *result;
274 TRACE(
"Raw headers: %s\n", rawHeaders.c_str());
280 std::string cleanPath =
path;
281 std::string queryString =
"";
283 size_t qpos = cleanPath.find(
'?');
284 if (qpos != std::string::npos)
286 queryString = cleanPath.substr(qpos + 1);
287 cleanPath = cleanPath.substr(0, qpos);
295 TRACE(
"Parsed headers:\n");
296 for (
const auto &header :
headers)
298 TRACE(
"%s: %s\n", header.first.c_str(), header.second.c_str());
302 TRACE(
"Content-Length: %d\n", contentLength);
304 if (contentLength > 0)
306 TRACE(
"Content-Length is greater than 0\n");
309 TRACE(
"Multipart request detected\n");
313 TRACE(
"Non-multipart request detected\n");
316 TRACE(
"Final body length: %zu\n",
body.length());
317 TRACE(
"HttpRequest object constructed\n");
342 std::unordered_map<std::string, std::string> cookies;
343 std::string cookieHeader =
getHeader(
"cookie");
344 std::istringstream stream(cookieHeader);
346 while (std::getline(stream, pair,
';'))
348 size_t eq = pair.find(
'=');
349 if (eq != std::string::npos)
351 std::string key = pair.substr(0, eq);
352 std::string value = pair.substr(eq + 1);
353 key.erase(0, key.find_first_not_of(
" \t"));
354 value.erase(0, value.find_first_not_of(
" \t"));
355 cookies[key] = value;
370 auto it = cookies.find(name);
371 return (it != cookies.end()) ? it->second :
"";
411 const auto proto_end =
uri.find(
"://");
412 if (proto_end == std::string::npos)
421 std::string rest =
uri.substr(proto_end + 3);
423 const auto path_start = rest.find(
'/');
424 if (path_start == std::string::npos)
431 host = rest.substr(0, path_start);
432 this->uri = rest.substr(path_start);
452 for (
const auto &[k, v] :
headers)
454 this->headers[k] = v;
467 headers[
"User-Agent"] = userAgent;
473 headers[
"Accept-Encoding"] = encoding;
482#if defined(PICO_HTTP_ENABLE_STORAGE)
484HttpRequest &HttpRequest::setBodyFromFile(
const std::string &path)
486 std::ifstream file(
path, std::ios::in | std::ios::binary);
489 body.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
494bool HttpRequest::setRootCACertificateFromFile(
const char *path)
496 std::string contents;
497 if (!StorageManager::instance().readFileToString(
path, contents))
Macro-based debug trace system with optional SD file logging.
#define TRACE_INIT(MODULE_NAME)
Declare trace usage in a source file for a given module.
#define TRACE(...)
Default trace (INFO level).
constexpr size_t MAX_HEADER_BYTES
HTTP Server class that listens for connections and dispatches requests to a Router.
Parser for handling multipart/form-data file uploads. Part of the PicoFramework HTTP server....
General-purpose TCP socket abstraction with optional TLS support for both client and server use.
static std::map< std::string, std::string > parseHeaders(const std::string &rawHeaders)
Parses the HTTP headers from a raw header string.
Forward declaration for potential routing needs.
HttpResponse get()
Send a GETPOST/PUT/DEL request.
const std::map< std::string, std::string > & getHeaders() const
Get all request headers.
HttpRequest & setUserAgent(const std::string &userAgent)
std::string getOutputFilePath() const
void setQueryString(const std::string &query)
HttpRequest & setUri(const std::string &uri)
Set the URI for the request.
static bool getMethodAndPath(const std::string &data, std::string &method, std::string &path)
Parse the HTTP method and path from the first request line.
std::string outputFilePath
HttpRequest & setRootCACertificate(const std::string &certData)
Set the root CA certificate to use for TLS.
HttpRequest & setHost(const std::string &host)
HttpRequest & setMethod(const std::string &method)
Set the HTTP method (e.g., GET, POST).
HttpRequest & setPath(const std::string &path)
Set the request path.
void parseHeaders(const char *raw)
HttpRequest & toFile(const std::string &path)
const std::unordered_multimap< std::string, std::string > getFormParams()
Get parsed form fields (application/x-www-form-urlencoded).
const std::unordered_multimap< std::string, std::string > getQueryParams()
Get parsed query string parameters.
static HttpRequest receive(Tcp *tcp)
Receive and parse an HTTP request from a socket.
int handle_multipart(HttpResponse &res)
Handle multipart/form-data uploads.
const std::unordered_map< std::string, std::string > getCookies() const
Get all parsed cookies.
static std::optional< std::pair< std::string, std::string > > receiveUntilHeadersComplete(Tcp *conn)
HttpRequest & setProtocol(const std::string &protocol)
std::map< std::string, std::string > headers
const std::string getCookie(const std::string &name) const
Get a specific cookie value.
int getContentLength() const
Get the Content-Length header as integer.
HttpResponse send()
Send the request and return the response.
HttpRequest & setHeaders(const std::map< std::string, std::string > &headers)
bool isMultipart() const
Check whether the request is multipart/form-data.
std::string rootCACertificate
HttpRequest & setAcceptEncoding(const std::string &encoding)
std::string getHeader(const std::string &field) const
Get a specific header field (case-insensitive).
static HttpRequest create()
HttpRequest & setHeader(const std::string &key, const std::string &value)
bool appendRemainingBody(int expectedLength)
HttpRequest & setBody(const std::string &body)
Set the body of the request.
Represents an HTTP response object.
Parses and processes multipart/form-data uploads over HTTP.
bool handleMultipart(HttpRequest &req, HttpResponse &res)
Begin processing the multipart upload from the socket.
General-purpose TCP socket wrapper with optional TLS support via mbedTLS (altcp).
int recv(char *buffer, size_t size, uint32_t timeout_ms)
Receive data from the connection.
int getSocketFd() const
Get the raw socket file descriptor (may be -1 for TLS-only connection).
Delegates to user or system configuration.
#define HTTP_BUFFER_SIZE
Size of the HTTP buffer for request/response data.
#define HTTP_RECEIVE_TIMEOUT
Timeout for receiving HTTP data in milliseconds.
#define MAX_HTTP_BODY_LENGTH
Maximum HTTP body size in bytes.
std::unordered_multimap< std::string, std::string > parseUrlEncoded(const std::string &data)
Utility functions for URL decoding, form parsing, MIME type lookup, and IP address extraction.
System utilities for diagnostics, memory, stack usage, and tracing.