8#ifdef FASTLED_HAS_NETWORKING
15 #ifndef WIN32_LEAN_AND_MEAN
16 #define WIN32_LEAN_AND_MEAN
34 #define boolean _FL_BOOLEAN_WORKAROUND
35 #define CRGB _FL_CRGB_WORKAROUND
46 #define SOCKET_ERROR_WOULD_BLOCK WSAEWOULDBLOCK
47 #define GET_SOCKET_ERROR() WSAGetLastError()
48 #define CLOSE_SOCKET(s) closesocket(s)
51 #include <sys/socket.h>
52 #include <sys/select.h>
54 #include <netinet/in.h>
61 #define SOCKET_ERROR_WOULD_BLOCK EWOULDBLOCK
62 #define GET_SOCKET_ERROR() errno
63 #define CLOSE_SOCKET(s) close(s)
82 , mStateStartTime(fl::
millis())
84 if (!mParsedUrl.isValid() || mParsedUrl.host().empty()) {
85 complete_error(
"Invalid URL");
88 mHostname =
fl::string(mParsedUrl.host().data(), mParsedUrl.host().size());
89 mPort = mParsedUrl.port();
98void FetchRequest::update() {
119void FetchRequest::handle_dns_lookup() {
123 FL_WARN(
"[FETCH] Resolving hostname: " << mHostname);
127 mDnsResult = gethostbyname(mHostname.c_str());
133 complete_error(
"DNS lookup failed");
138 mSocketFd = socket(AF_INET, SOCK_STREAM, 0);
140 complete_error(
"Failed to create socket");
147 ioctlsocket(mSocketFd, FIONBIO, &mode);
149 int flags = fcntl(mSocketFd, F_GETFL, 0);
150 fcntl(mSocketFd, F_SETFL, flags | O_NONBLOCK);
154 sockaddr_in server_addr{};
155 server_addr.sin_family = AF_INET;
156 server_addr.sin_port = htons(
static_cast<u16
>(mPort));
159 char *addr_ptr =
nullptr;
160 fl::memcpy(&addr_ptr, mDnsResult->h_addr_list,
sizeof(addr_ptr));
161 fl::memcpy(&server_addr.sin_addr, addr_ptr, mDnsResult->h_length);
163 FL_WARN(
"[FETCH] Waiting for connection to " << mHostname <<
":" << mPort);
165 connect(mSocketFd, (sockaddr*)&server_addr,
sizeof(server_addr));
172void FetchRequest::handle_connecting() {
175 FD_SET(mSocketFd, &write_fds);
181 int result = select(mSocketFd + 1,
nullptr, &write_fds,
nullptr, &timeout);
186 socklen_t len =
sizeof(sock_err);
187 getsockopt(mSocketFd, SOL_SOCKET, SO_ERROR, (
char*)&sock_err, &len);
190 complete_error(
"Connection failed");
195 mRequestBuffer =
"GET " + mPath +
" HTTP/1.1\r\n";
196 mRequestBuffer +=
"Host: " + mHostname +
"\r\n";
197 mRequestBuffer +=
"Connection: close\r\n\r\n";
202 }
else if (result < 0) {
203 complete_error(
"select() failed during connection");
207 complete_error(
"Connection timeout");
213void FetchRequest::handle_sending() {
214 ssize_t sent = send(mSocketFd,
215 mRequestBuffer.c_str() + mBytesSent,
216 mRequestBuffer.size() - mBytesSent,
221 if (mBytesSent >= mRequestBuffer.size()) {
222 FL_WARN(
"[FETCH] Waiting for HTTP response...");
226 }
else if (sent < 0) {
227 int err = GET_SOCKET_ERROR();
228 if (err != SOCKET_ERROR_WOULD_BLOCK) {
229 complete_error(
"Send failed");
235void FetchRequest::handle_receiving() {
237 ssize_t bytes = recv(mSocketFd, buffer,
sizeof(buffer), 0);
240 mResponseBuffer.append(buffer,
static_cast<size_t>(bytes));
242 }
else if (bytes == 0) {
244 Response resp = parse_http_response(mResponseBuffer);
245 complete_success(resp);
247 int err = GET_SOCKET_ERROR();
248 if (err == SOCKET_ERROR_WOULD_BLOCK) {
251 complete_error(
"Response timeout");
255 complete_error(
"Receive failed");
260Response FetchRequest::parse_http_response(
const fl::string& raw) {
261 const char* data = raw.
c_str();
262 const size_t len = raw.
size();
265 size_t header_end = raw.
find(
"\r\n\r\n");
267 return Response(500,
"Internal Server Error");
271 size_t first_space = raw.
find(
' ');
272 size_t second_space = raw.
find(
' ', first_space + 1);
274 first_space >= header_end || second_space >= header_end) {
275 return Response(500,
"Internal Server Error");
280 for (
size_t i = first_space + 1; i < second_space; ++i) {
281 status_code = status_code * 10 + (data[i] -
'0');
285 size_t status_line_end = raw.
find(
"\r\n");
286 size_t st_len = (status_line_end !=
fl::string::npos ? status_line_end : header_end) - (second_space + 1);
288 Response resp(status_code,
fl::string(data + second_space + 1, st_len));
289 resp.set_body(
fl::string(data + header_end + 4, len - header_end - 4));
292 size_t pos = status_line_end + 2;
293 while (
pos < header_end) {
294 size_t line_end = raw.
find(
"\r\n",
pos);
296 line_end = header_end;
301 for (
size_t i =
pos; i < line_end; ++i) {
302 if (data[i] ==
':') {
311 for (
size_t i = 0; i < name.size(); ++i) {
312 if (name[i] >=
'A' && name[i] <=
'Z') {
313 name[i] += (
'a' -
'A');
318 size_t val_start = colon + 1;
319 if (val_start < line_end && data[val_start] ==
' ') {
322 resp.set_header(name,
fl::string(data + val_start, line_end - val_start));
331void FetchRequest::complete_success(
const Response& resp) {
335 if (mPromise.valid() && !mPromise.is_completed()) {
336 mPromise.complete_with_value(resp);
340void FetchRequest::complete_error(
const char* message) {
344 if (mPromise.valid() && !mPromise.is_completed()) {
349void FetchRequest::close_socket() {
350 if (mSocketFd >= 0) {
351 CLOSE_SOCKET(mSocketFd);
fl::size find(const char &value) const FL_NOEXCEPT
const char * c_str() const FL_NOEXCEPT
fl::size size() const FL_NOEXCEPT
Fetch options builder (fluent interface)
FetchRequest(const fl::string &url, const FetchOptions &opts, fl::task::Promise< Response > promise)
Construct a new fetch request.
constexpr bool empty() const FL_NOEXCEPT
static constexpr fl::size npos
Task executor — runs registered task runners and manages the run loop.
Centralized logging categories for FastLED hardware interfaces and subsystems.
void run(fl::u32 microseconds, ExecFlags flags)
Run selected task subsystems.
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
expected< T, E > result
Alias for expected (Rust-style naming)
Base definition for an LED controller.