mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-23 17:09:44 +00:00
092cac9073
byuu says: Changelog: - synchronizes lots of nall changes - changes displayed program title from tomoko to higan(*) - browser dialog sort is case-insensitive - .sys folders look at user-selected library path; no longer hard-coded Tried to get rid of the file modes from the Windows browser dialog, but it was being a bitch so I left it on for now. - The storage locations and binary still use tomoko. I'm not really sure what to do here. The idea is there may be more than one "higan" UI in the future, but I don't want people to go around calling the entire program by the UI name. For official Windows releases, I can rename the binaries to "higan-{profile}.exe", and by putting the config files with the binary, they won't ever see the tomoko folder. Linux is of course trickier. Note: Windows users will need to edit hiro/components.hpp and comment out these lines: #define Hiro_Console #define Hiro_IconView #define Hiro_SourceView #define Hiro_TreeView I forgot to do that, and too lazy to upload another WIP.
212 lines
5.9 KiB
C++
212 lines
5.9 KiB
C++
#ifndef NALL_HTTP_SERVER_HPP
|
|
#define NALL_HTTP_SERVER_HPP
|
|
|
|
#include <nall/service.hpp>
|
|
#include <nall/http/role.hpp>
|
|
|
|
namespace nall {
|
|
|
|
struct httpServer : httpRole, service {
|
|
inline auto open(unsigned port = 8080, const string& serviceName = "", const string& command = "") -> bool;
|
|
inline auto main(const function<httpResponse (httpRequest&)>& function = {}) -> void;
|
|
inline auto scan() -> string;
|
|
inline auto close() -> void;
|
|
~httpServer() { close(); }
|
|
|
|
private:
|
|
function<httpResponse (httpRequest&)> callback;
|
|
std::atomic<signed> connections{0};
|
|
|
|
signed fd4 = -1;
|
|
signed fd6 = -1;
|
|
struct sockaddr_in addrin4 = {0};
|
|
struct sockaddr_in6 addrin6 = {0};
|
|
|
|
auto ipv4() const -> bool { return fd4 >= 0; }
|
|
auto ipv6() const -> bool { return fd6 >= 0; }
|
|
|
|
auto ipv4_close() -> void { if(fd4 >= 0) ::close(fd4); fd4 = -1; }
|
|
auto ipv6_close() -> void { if(fd6 >= 0) ::close(fd6); fd6 = -1; }
|
|
|
|
auto ipv4_scan() -> bool;
|
|
auto ipv6_scan() -> bool;
|
|
};
|
|
|
|
auto httpServer::open(unsigned port, const string& serviceName, const string& command) -> bool {
|
|
if(serviceName) {
|
|
if(!service::command(serviceName, command)) return false;
|
|
}
|
|
|
|
fd4 = socket(AF_INET, SOCK_STREAM, 0);
|
|
fd6 = socket(AF_INET6, SOCK_STREAM, 0);
|
|
if(!ipv4() && !ipv6()) return false;
|
|
|
|
{
|
|
#if defined(SO_RCVTIMEO)
|
|
if(settings.timeoutReceive) {
|
|
struct timeval rcvtimeo;
|
|
rcvtimeo.tv_sec = settings.timeoutReceive / 1000;
|
|
rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000;
|
|
if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval));
|
|
if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval));
|
|
}
|
|
#endif
|
|
|
|
#if defined(SO_SNDTIMEO)
|
|
if(settings.timeoutSend) {
|
|
struct timeval sndtimeo;
|
|
sndtimeo.tv_sec = settings.timeoutSend / 1000;
|
|
sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000;
|
|
if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval));
|
|
if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval));
|
|
}
|
|
#endif
|
|
|
|
#if defined(SO_NOSIGPIPE) //BSD, OSX
|
|
signed nosigpipe = 1;
|
|
if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(signed));
|
|
if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(signed));
|
|
#endif
|
|
|
|
#if defined(SO_REUSEADDR) //BSD, Linux, OSX
|
|
signed reuseaddr = 1;
|
|
if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(signed));
|
|
if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(signed));
|
|
#endif
|
|
|
|
#if defined(SO_REUSEPORT) //BSD, OSX
|
|
signed reuseport = 1;
|
|
if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(signed));
|
|
if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(signed));
|
|
#endif
|
|
}
|
|
|
|
addrin4.sin_family = AF_INET;
|
|
addrin4.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addrin4.sin_port = htons(port);
|
|
|
|
addrin6.sin6_family = AF_INET6;
|
|
addrin6.sin6_addr = in6addr_any;
|
|
addrin6.sin6_port = htons(port);
|
|
|
|
if(bind(fd4, (struct sockaddr*)&addrin4, sizeof(addrin4)) < 0 || listen(fd4, SOMAXCONN) < 0) ipv4_close();
|
|
if(bind(fd6, (struct sockaddr*)&addrin6, sizeof(addrin6)) < 0 || listen(fd6, SOMAXCONN) < 0) ipv6_close();
|
|
return ipv4() || ipv6();
|
|
}
|
|
|
|
auto httpServer::main(const function<httpResponse (httpRequest&)>& function) -> void {
|
|
callback = function;
|
|
}
|
|
|
|
auto httpServer::scan() -> string {
|
|
if(auto command = service::receive()) return command;
|
|
if(connections >= settings.connectionLimit) return "busy";
|
|
if(ipv4() && ipv4_scan()) return "ok";
|
|
if(ipv6() && ipv6_scan()) return "ok";
|
|
return "idle";
|
|
}
|
|
|
|
auto httpServer::ipv4_scan() -> bool {
|
|
struct pollfd query = {0};
|
|
query.fd = fd4;
|
|
query.events = POLLIN;
|
|
poll(&query, 1, 0);
|
|
|
|
if(query.fd == fd4 && query.revents & POLLIN) {
|
|
++connections;
|
|
|
|
thread::create([&](uintptr_t) {
|
|
thread::detach();
|
|
|
|
signed clientfd = -1;
|
|
struct sockaddr_in settings = {0};
|
|
socklen_t socklen = sizeof(sockaddr_in);
|
|
|
|
clientfd = accept(fd4, (struct sockaddr*)&settings, &socklen);
|
|
if(clientfd < 0) return;
|
|
|
|
uint32_t ip = ntohl(settings.sin_addr.s_addr);
|
|
|
|
httpRequest request;
|
|
request._ipv6 = false;
|
|
request._ip = {
|
|
(uint8_t)(ip >> 24), ".",
|
|
(uint8_t)(ip >> 16), ".",
|
|
(uint8_t)(ip >> 8), ".",
|
|
(uint8_t)(ip >> 0)
|
|
};
|
|
|
|
if(download(clientfd, request) && callback) {
|
|
auto response = callback(request);
|
|
upload(clientfd, response);
|
|
} else {
|
|
upload(clientfd, httpResponse()); //"501 Not Implemented"
|
|
}
|
|
|
|
::close(clientfd);
|
|
--connections;
|
|
}, 0, settings.threadStackSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto httpServer::ipv6_scan() -> bool {
|
|
struct pollfd query = {0};
|
|
query.fd = fd6;
|
|
query.events = POLLIN;
|
|
poll(&query, 1, 0);
|
|
|
|
if(query.fd == fd6 && query.revents & POLLIN) {
|
|
++connections;
|
|
|
|
thread::create([&](uintptr_t) {
|
|
thread::detach();
|
|
|
|
signed clientfd = -1;
|
|
struct sockaddr_in6 settings = {0};
|
|
socklen_t socklen = sizeof(sockaddr_in6);
|
|
|
|
clientfd = accept(fd6, (struct sockaddr*)&settings, &socklen);
|
|
if(clientfd < 0) return;
|
|
|
|
unsigned char* ip = settings.sin6_addr.s6_addr;
|
|
uint16_t ipSegment[8] = {0};
|
|
for(auto n : range(8)) ipSegment[n] = ip[n * 2 + 0] * 256 + ip[n * 2 + 1];
|
|
|
|
httpRequest request;
|
|
request._ipv6 = true;
|
|
for(auto n : range(8)) {
|
|
uint16_t value = ip[n * 2 + 0] * 256 + ip[n * 2 + 1];
|
|
request._ip.append(hex(value, 4L));
|
|
if(n != 7) request._ip.append(":");
|
|
}
|
|
|
|
if(download(clientfd, request) && callback) {
|
|
auto response = callback(request);
|
|
upload(clientfd, response);
|
|
} else {
|
|
upload(clientfd, httpResponse()); //"501 Not Implemented"
|
|
}
|
|
|
|
::close(clientfd);
|
|
--connections;
|
|
}, 0, settings.threadStackSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto httpServer::close() -> void {
|
|
ipv4_close();
|
|
ipv6_close();
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|