http part 2

This commit is contained in:
darkrose 2013-07-20 14:46:48 +10:00
parent 8c213bfeed
commit 1c5b35e18d
12 changed files with 746 additions and 7 deletions

View File

@ -158,6 +158,8 @@
#time_send_interval = 20
# Length of day/night cycle. 72=20min, 360=4min, 1=24hour
#time_speed = 72
# enable a http microserver for admin and player textures
#enable_http = true
#server_unload_unused_data_timeout = 60
#server_map_save_interval = 60
#full_block_send_enable_min_time_from_building = 2.0

View File

@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "profiler.h"
#include "log.h"
#include "http.h"
/*
QueuedMeshUpdate
@ -211,6 +212,8 @@ Client::Client(
//m_env_mutex.Init();
//m_con_mutex.Init();
m_httpclient = new HTTPClient(this);
m_mesh_update_thread.Start();
/*
@ -248,6 +251,8 @@ void Client::connect(Address address)
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
m_con.SetTimeoutMs(0);
m_con.Connect(address);
if (g_settings->getBool("enable_http"))
m_httpclient->start(address);
}
bool Client::connectedAndInitialized()
@ -760,6 +765,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
writeU16(&reply[0], TOSERVER_INIT2);
// Send as reliable
m_con.Send(PEER_ID_SERVER, 1, reply, true);
sendWantCookie();
return;
}
@ -1046,6 +1052,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
}
player->updateName((char*)&data[start+2]);
std::string p_name((char*)&data[start+2]);
m_httpclient->pushRequest(HTTPREQUEST_SKIN_HASH,p_name);
start += item_size;
}
@ -1507,6 +1515,22 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
event.deathscreen.camera_point_target_z = camera_point_target.Z;
m_client_event_queue.push_back(event);
}
else if(command == TOCLIENT_HAVECOOKIE)
{
/*
u16 command
u16 textlen
textdata
*/
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u16 len = readU16(is);
char buff[len+1];
is.read(buff,len);
buff[len] = 0;
printf("received cookie '%s'\n",buff);
}
else
{
infostream<<"Client: Ignoring unknown command "
@ -1756,6 +1780,19 @@ void Client::sendRespawn()
Send(0, data, true);
}
void Client::sendWantCookie()
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_WANTCOOKIE);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
Send(0, data, true);
}
void Client::sendPlayerPos()
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

View File

@ -139,6 +139,8 @@ struct ClientEvent
};
};
class HTTPClient;
class Client : public con::PeerHandler, public InventoryManager
{
public:
@ -199,6 +201,7 @@ public:
const std::wstring newpassword);
void sendDamage(u8 damage);
void sendRespawn();
void sendWantCookie();
// locks envlock
void removeNode(v3s16 p);
@ -330,6 +333,7 @@ private:
ClientEnvironment m_env;
con::Connection m_con;
HTTPClient *m_httpclient;
IrrlichtDevice *m_device;

View File

@ -191,6 +191,13 @@ enum ToClientCommand
u8 bool set camera point target
v3f1000 camera point target (to point the death cause or whatever)
*/
TOCLIENT_HAVECOOKIE = 0x38,
/*
u16 command
u16 textlen
textdata
*/
};
enum ToServerCommand
@ -344,6 +351,11 @@ enum ToServerCommand
/*
u16 TOSERVER_RESPAWN
*/
TOSERVER_WANTCOOKIE=0x39,
/*
u16 TOSERVER_WANTCOOKIE
*/
};
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)

View File

@ -96,6 +96,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("default_privs", "build, shout");
settings->setDefault("borderstone_radius","5");
settings->setDefault("enable_http","true");
settings->setDefault("profiler_print_interval", "0");
settings->setDefault("enable_mapgen_debug_info", "false");
settings->setDefault("objectdata_interval", "0.2");

View File

@ -27,3 +27,478 @@
#include <stdlib.h>
#include <errno.h>
#include "utility.h"
#include "connection.h"
#include "log.h"
#include "sha1.h"
void * HTTPServerThread::Thread()
{
ThreadStarted();
log_register_thread("HTTPServerThread");
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
while (getRun())
{
try{
m_server->step();
}catch (con::NoIncomingDataException &e) {
}catch(con::PeerNotFoundException &e) {
}
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
}
/*
* HTTPServer
*/
HTTPServer::HTTPServer(Server &server):
m_thread(this)
{
m_server = &server;
}
HTTPServer::~HTTPServer()
{
}
void HTTPServer::start(u16 port)
{
DSTACK(__FUNCTION_NAME);
// Stop thread if already running
m_thread.stop();
m_socket = new TCPSocket();
m_socket->setTimeoutMs(30);
m_socket->Bind(port);
// Start thread
m_thread.setRun(true);
m_thread.Start();
infostream<<"HTTPServer: Started on port "<<port<<std::endl;
}
void HTTPServer::stop()
{
DSTACK(__FUNCTION_NAME);
m_thread.stop();
delete m_socket;
infostream<<"HTTPServer: Threads stopped"<<std::endl;
}
void HTTPServer::step()
{
if (m_socket->WaitData(50)) {
TCPSocket *s = m_socket->Accept();
HTTPRemoteClient *c = new HTTPRemoteClient(s,this);
m_peers.push_back(c);
}
for (std::vector<HTTPRemoteClient*>::iterator it = m_peers.begin(); it != m_peers.end(); ++it) {
HTTPRemoteClient *c = *it;
try{
if (c->receive()) {
m_peers.erase(it);
delete c;
}
}catch (SocketException &e) {
// assume it's closed
m_peers.erase(it);
delete c;
continue;
}
}
}
/*
* HTTPRemoteClient
*/
HTTPRemoteClient::~HTTPRemoteClient()
{
}
int HTTPRemoteClient::receive()
{
char buff[2048];
if (!m_socket->WaitData(30))
return 0;
int r = m_socket->Receive(buff,2048);
if (r<1)
return 1;
m_recv_headers.clear();
m_send_headers.clear();
int h = m_recv_headers.read(buff,r);
if (h == 1) {
return 1;
}else if (h == 2) {
//if (m_recv_headers.url() == "")
//return 1;
//while (m_socket->WaitData(1000)) {
//r = m_socket->Receive(buff,2048);
//if (r<1)
//return 1;
//h = m_recv_headers.read(buff,r);
//if (!h)
//break;
//if (h == 1)
//return 1;
//}
//if (h)
return 1;
}
if (m_recv_headers.cookie() != "" && m_recv_headers.get("User") != "") {
if (m_recv_headers.cookie() == m_server->getPlayerCookie(m_recv_headers.get("User"))) {
m_send_headers.setCookie(m_recv_headers.cookie());
m_auth = true;
}
}
//std::string u;
//for (int i=0; (u = m_recv_headers.url(i)) != ""; i++) {
//printf("%d: '%s'\n",i,u.c_str());
//}
//setResponse("404 Not Found");
//std::string html("<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>");
//send(html);
send((char*)"grar");
return 0;//m_recv_headers.keepAlive() == true ? 0 : 1;
}
void HTTPRemoteClient::send(char* data)
{
int l = strlen(data);
m_send_headers.set("Content-Type","text/plain");
m_send_headers.setLength(l);
sendHeaders();
m_socket->Send(data,l);
}
void HTTPRemoteClient::sendHTML(char* data)
{
int l = strlen(data);
m_send_headers.set("Content-Type","text/html");
m_send_headers.setLength(l);
sendHeaders();
m_socket->Send(data,l);
}
void HTTPRemoteClient::sendFile(std::string &file)
{
m_send_headers.set("Content-Type","text/plain");
m_send_headers.setLength(0);
sendHeaders();
}
void HTTPRemoteClient::sendHeaders()
{
std::string v;
int s;
char buff[1024];
//v = m_response;
//if (v == "")
//v = std::string("200 OK");
//s = snprintf(buff,1024,"HTTP/1.1 %s\r\n",v.c_str());
//m_socket->Send(buff,s);
m_socket->Send("HTTP/1.1 200 OK\r\n",17);
//v = m_send_headers.get("Content-Type");
//if (v == "") {
m_socket->Send("Content-Type: text/plain\r\n",26);
//}else{
//s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str());
//m_socket->Send(buff,s);
//}
s = m_send_headers.length();
s = snprintf(buff,1024,"Content-Length: %d\r\n",s);
m_socket->Send(buff,s);
//v = m_send_headers.cookie();
//if (v != "") {
//s = snprintf(buff,1024,"Set-Cookie: MTID=%s\r\n",v.c_str());
//m_socket->Send(buff,s);
//}
m_socket->Send("\r\n",2);
}
#ifndef SERVER
#include "client.h"
void * HTTPClientThread::Thread()
{
ThreadStarted();
log_register_thread("HTTPClientThread");
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
while (getRun())
{
try{
m_client->step();
}catch (con::NoIncomingDataException &e) {
}catch(con::PeerNotFoundException &e) {
}
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
}
/*
* HTTPClient
*/
HTTPClient::HTTPClient(Client *client):
m_cookie(""),
m_thread(this)
{
m_client = client;
m_req_mutex.Init();
}
HTTPClient::~HTTPClient()
{
}
void HTTPClient::start(const Address &address)
{
DSTACK(__FUNCTION_NAME);
// Stop thread if already running
m_thread.stop();
m_socket = new TCPSocket();
m_socket->setTimeoutMs(30);
m_socket->Connect(address);
// Start thread
m_thread.setRun(true);
m_thread.Start();
infostream<<"HTTPClient: Started"<<std::endl;
}
void HTTPClient::stop()
{
DSTACK(__FUNCTION_NAME);
m_thread.stop();
delete m_socket;
infostream<<"HTTPClient: Threads stopped"<<std::endl;
}
void HTTPClient::step()
{
sleep(1);
}
void HTTPClient::pushRequest(HTTPRequestType type, std::string &data)
{
switch (type) {
case HTTPREQUEST_NULL:
break;
/*
* /player/<name>/skin
* request the skin texture for <name>
* response:
* if skin exists:
* 200 OK + texture data
* else:
* 303 See Other + Location header to /texture/character.png
* this client will ignore the redirect, while browsers will see the default skin
*/
case HTTPREQUEST_SKIN:
{
std::string url("/player/");
url += data + "/skin";
HTTPRequest r(url);
m_req_mutex.Lock();
m_requests.push_back(r);
m_req_mutex.Unlock();
break;
}
/*
* /player/<name>/skin/hash/<sha1>
* sends the sha1 hash of the skin texture for <name>
* response:
* if hash is the same on client and server:
* 204 No Content
* if client is authenticated as <name>:
* 304 Not Modified
* else:
* 200 OK + texture data
*/
case HTTPREQUEST_SKIN_HASH:
{
std::string tex = std::string("players/player_")+data+".png";
std::string ptex = getTexturePath(tex);
if (ptex == "") {
pushRequest(HTTPREQUEST_SKIN,data);
return;
}
break;
}
/*
* /player/<name>/skin
* PUT request type, sends the skin to the server
* response:
* if client is authenticated as <name>:
* 201 Created
* else:
* 405 Method Not Allowed
*/
case HTTPREQUEST_SKIN_SEND:
{
break;
}
default:;
}
}
void HTTPClient::pushRequest(std::string &url, std::string &data)
{
HTTPRequest r(url,data);
m_req_mutex.Lock();
m_requests.push_back(r);
m_req_mutex.Unlock();
}
void HTTPClient::get(std::string &url)
{
}
void HTTPClient::post(std::string &url, char* data)
{
}
void HTTPClient::postFile(std::string &url, std::string &file)
{
}
HTTPRequest HTTPClient::popRequest()
{
m_req_mutex.Lock();
HTTPRequest r = m_requests.front();
m_requests.erase(m_requests.begin());
m_req_mutex.Unlock();
return r;
}
void HTTPClient::sendHeaders()
{
}
#endif
/*
* HTTPHeaders
*/
int HTTPHeaders::read(char* buff, int length)
{
char nbuff[1024];
char vbuff[1024];
int n = 1;
int o = 0;
int c = m_url == "" ? 0 : 1;
for (int i=0; i<length; i++) {
if (buff[i] == '\r' || (!o && buff[i] == ' '))
continue;
if (buff[i] == '\n') {
if (!c) {
nbuff[o] = 0;
printf("%s\n",nbuff);
char* u = strchr(nbuff,' ');
if (!u)
return 1;
*u = 0;
setMethod(nbuff);
u++;
while (*u == ' ') {
u++;
}
printf("%s\n",u);
char* p = strchr(u,' ');
if (!p)
return 1;
*p = 0;
p++;
while (*p == ' ') {
p++;
}
printf("%s\n",p);
if (!strcmp(p,"HTTP/1.1")) {
setKeepAlive(true);
setProtocol(p);
}else{
setKeepAlive(false);
setProtocol("HTTP/1.0");
}
setUrl(nbuff);
//size_t current;
//size_t next = -1;
//std::string s(u);
//do{
//current = next + 1;
//next = s.find_first_of("/", current);
//if (s.substr(current, next-current) != "")
//m_url_split.push_back(s.substr(current, next-current));
//} while (next != std::string::npos);
c++;
}else{
if (n)
return 0;
vbuff[o] = 0;
if (!strcmp(nbuff,"Content-Length")) {
setLength(strtoul(vbuff,NULL,10));
}else if (!strcmp(nbuff,"Cookie")) {
setCookie(vbuff);
}else if (!strcmp(nbuff,"Connection")) {
if (!strcmp(vbuff,"keep-alive") || !strcmp(vbuff,"Keep-Alive")) {
setKeepAlive(true);
}else if (!strcmp(vbuff,"close") || !strcmp(vbuff,"Close")) {
setKeepAlive(false);
}
}else{
set(nbuff,vbuff);
}
}
o = 0;
n = 1;
}else if (n && buff[i] == ':') {
nbuff[o] = 0;
o = 0;
n = 0;
}else if (n) {
nbuff[o++] = buff[i];
}else{
vbuff[o++] = buff[i];
}
}
return 2;
}

View File

@ -25,24 +25,185 @@
#include "socket.h"
#include "common_irrlicht.h"
#include "utility.h"
#include "server.h"
#include <map>
class HTTPServer;
class HTTPServerThread : public SimpleThread
{
HTTPServer *m_server;
public:
HTTPServerThread(HTTPServer *server):
SimpleThread(),
m_server(server)
{
}
void * Thread();
};
class HTTPHeaders
{
public:
HTTPHeaders()
{
clear();
}
~HTTPHeaders() {};
void clear()
{
m_contentLength = 0;
m_keepalive = false;
m_cookie = std::string("");
m_url = std::string("");
m_url_split.clear();
}
int read(char* buff, int length);
u32 length() {return m_contentLength;}
bool keepAlive() {return m_keepalive;}
std::string &cookie() {return m_cookie;}
std::string &url() {return m_url;}
std::string &url(int index) {return m_url_split[index];}
std::string &get(std::string name) {return m_headers[name];}
void set(std::string name, std::string value) {m_headers[name] = value;}
void setLength(u32 length) {m_contentLength = length;}
void setKeepAlive(bool ka) {m_keepalive = ka;}
void setCookie(std::string cookie) {std::string c(cookie.c_str()); m_cookie = c;}
void setUrl(std::string url) {std::string u(url); m_url = u;}
void setMethod(std::string method) {std::string m(method); m_method = m;}
void setProtocol(std::string proto) {std::string p(proto); m_protocol = p;}
private:
std::map<std::string,std::string> m_headers;
u32 m_contentLength;
bool m_keepalive;
std::string m_cookie;
std::string m_url;
std::vector<std::string> m_url_split;
std::string m_method;
std::string m_protocol;
};
class HTTPRemoteClient
{
public:
HTTPRemoteClient(TCPSocket *sock, HTTPServer *server):
m_response(""),
m_auth(false)
{
m_socket = sock;
m_server = server;
}
~HTTPRemoteClient();
int receive();
void send(char* data);
void send(std::string &data) {send((char*)data.c_str());}
void sendHTML(char* data);
void sendHTML(std::string &data) {sendHTML((char*)data.c_str());}
void sendFile(std::string &file);
void setResponse(const char* response) {std::string r(response); m_response = r;}
private:
void sendHeaders();
HTTPHeaders m_recv_headers;
HTTPHeaders m_send_headers;
std::string m_response;
bool m_auth;
TCPSocket *m_socket;
HTTPServer *m_server;
};
class HTTPServer
{
public:
HTTPServer();
HTTPServer(Server &server);
~HTTPServer();
void start(u16 port);
void stop();
void step();
std::string getPlayerCookie(std::string &name) {return m_server->getPlayerCookie(name);}
private:
TCPSocket &m_socket;
core::map<u16,TCPSocket&> m_peers;
TCPSocket *m_socket;
std::vector<HTTPRemoteClient*> m_peers;
HTTPServerThread m_thread;
Server *m_server;
};
enum HTTPRequestType {
HTTPREQUEST_NULL,
HTTPREQUEST_SKIN,
HTTPREQUEST_SKIN_HASH,
HTTPREQUEST_SKIN_SEND
};
struct HTTPRequest
{
HTTPRequest():
url(""),
post("")
{
}
HTTPRequest(std::string &u, std::string &p)
{
url = u;
post = p;
}
HTTPRequest(std::string &u)
{
url = u;
post = "";
}
std::string url;
std::string post;
};
#ifndef SERVER
#include "client.h"
class HTTPClient;
class HTTPClientThread : public SimpleThread
{
HTTPClient *m_client;
public:
HTTPClientThread(HTTPClient *client):
SimpleThread(),
m_client(client)
{
}
void * Thread();
};
class HTTPClient
{
public:
HTTPClient();
HTTPClient(Client *client);
~HTTPClient();
void start(const Address &address);
void stop();
void step();
void setCookie(std::string &cookie) {std::string c(cookie.c_str()); m_cookie = c;}
void pushRequest(HTTPRequestType type, std::string &data);
void pushRequest(std::string &url, std::string &data);
private:
TCPSocket &m_socket;
void get(std::string &url);
void post(std::string &url, char* data);
void postFile(std::string &url, std::string &file);
HTTPRequest popRequest();
void sendHeaders();
TCPSocket *m_socket;
HTTPHeaders m_recv_headers;
HTTPHeaders m_send_headers;
std::string m_cookie;
std::vector<HTTPRequest> m_requests;
JMutex m_req_mutex;
HTTPClientThread m_thread;
Client *m_client;
};
#endif
#endif

View File

@ -138,6 +138,7 @@ public:
{
light = light_at_pos;
}
virtual std::string getCookie() {return "";}
virtual void updateAnim(u8 anim_id)
{}
@ -194,7 +195,8 @@ class ServerRemotePlayer : public Player
{
public:
ServerRemotePlayer():
m_addr("")
m_addr(""),
m_cookie("")
{
}
virtual ~ServerRemotePlayer()
@ -214,7 +216,18 @@ public:
std::string getAddress() {return m_addr;}
private:
virtual std::string getCookie()
{
if (m_cookie == "") {
char buff[256];
snprintf(buff,256,"%s%.4X",getName(),peer_id);
m_cookie = std::string(buff);
}
return m_cookie;
}
std::string m_addr;
std::string m_cookie;
};
#ifndef SERVER

View File

@ -4228,6 +4228,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
actionstream<<player->getName()<<" respawns at "
<<PP(player->getPosition()/BS)<<std::endl;
}
else if(command == TOSERVER_WANTCOOKIE)
{
SendPlayerCookie(player);
}
else
{
infostream<<"Server::ProcessData(): Ignoring "
@ -4650,6 +4654,22 @@ void Server::SendPlayerHP(Player *player)
SendHP(m_con, player->peer_id, player->hp);
}
void Server::SendPlayerCookie(Player *player)
{
std::ostringstream os(std::ios_base::binary);
std::string c = player->getCookie();
writeU16(os, TOCLIENT_HAVECOOKIE);
writeU16(os, c.size());
os.write(c.c_str(),c.size());
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(player->peer_id, 0, data, true);
}
void Server::SendMovePlayer(Player *player)
{
DSTACK(__FUNCTION_NAME);

View File

@ -448,6 +448,14 @@ public:
}
}
std::string getPlayerCookie(std::string &name)
{
Player *p = m_env.getPlayer(name.c_str());
if (p == NULL)
return "";
return p->getCookie();
}
// Saves g_settings to configpath given at initialization
void saveConfig();
@ -511,6 +519,7 @@ private:
void SendChatMessage(u16 peer_id, const std::wstring &message);
void BroadcastChatMessage(const std::wstring &message);
void SendPlayerHP(Player *player);
void SendPlayerCookie(Player *player);
/*
Send a node removal/addition event to all clients except ignore_id.
Additionally, if far_players!=NULL, players further away than

View File

@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_craft.h"
#include "content_craftitem.h"
#include "content_toolitem.h"
#include "http.h"
/*
Settings.
@ -370,9 +371,13 @@ int main(int argc, char *argv[])
// Create server
Server server(map_dir.c_str(), configpath);
server.start(port);
HTTPServer http_server(server);
if (g_settings->getBool("enable_http"))
http_server.start(port);
// Run server
dedicated_server_loop(server, kill);
http_server.stop();
} //try
catch(con::PeerNotFoundException &e)

View File

@ -113,7 +113,7 @@ std::string getTexturePath(const std::string &filename)
Check from cache
*/
bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
if(incache)
if (incache && fullpath != "")
return fullpath;
std::string rel_path = std::string("textures")+DIR_DELIM+filename;