Added m_http_client - this is incomplete and won't work at all, don't try to use it

git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@6243 e03df62e-2008-0410-955e-edbf42e46eb7
This commit is contained in:
special 2007-01-07 03:50:18 +00:00
parent dab57ed68a
commit e513c4c3f9
2 changed files with 463 additions and 0 deletions

121
src/modules/httpclient.h Normal file
View File

@ -0,0 +1,121 @@
#include "base.h"
#ifndef HTTPCLIENT_H__
#define HTTPCLIENT_H__
#include <string>
#include <map>
typedef std::map<std::string,std::string> HeaderMap;
/** This class represents an outgoing HTTP request
*/
class HTTPClientRequest : public classbase
{
protected:
std::string url;
InspIRCd *Instance;
Module *src;
HeaderMap Headers;
public:
HTTPClientRequest(InspIRCd *Instance, Module *src, const std::string &url)
: url(url), Instance(Instance), src(src)
{
Headers["User-Agent"] = "InspIRCd (m_http_client.so)";
Headers["Connection"] = "Close";
Headers["Accept"] = "*/*";
}
const std::string &GetURL()
{
return url;
}
Module *GetSrc()
{
return src;
}
void AddHeader(std::string &header, std::string &data)
{
Headers[header] = data;
}
void DeleteHeader(std::string &header)
{
Headers.erase(header);
}
HeaderMap GetHeaders()
{
return Headers;
}
void SendRequest()
{
Module *HTTPModule = Instance->FindModule("m_http_client.so");
if (!HTTPModule)
{
Instance->Log(DEFAULT, "HTTP module not loaded!");
return;
}
Request req((char *)this, src, HTTPModule);
req.Send();
}
};
class HTTPClientResponse : public classbase
{
protected:
friend class HTTPSocket;
std::string url;
std::string data;
int response;
std::string responsestr;
HeaderMap Headers;
public:
HTTPClientResponse(std::string &url, int response, std::string responsestr)
: url(url), response(response), responsestr(responsestr)
{
}
void SetData(const std::string &ndata)
{
data = ndata;
}
void AddHeader(const std::string &header, const std::string &data)
{
Headers[header] = data;
}
const std::string &GetURL()
{
return url;
}
const std::string &GetData()
{
return data;
}
int GetResponse(std::string &str)
{
str = responsestr;
return response;
}
std::string GetHeader(const std::string &header)
{
HeaderMap::iterator i = Headers.find(header);
if (i != Headers.end())
return i->second;
else
return "";
}
};
#endif

View File

@ -0,0 +1,342 @@
/* +------------------------------------+
* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
* InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
* E-mail:
* <brain@chatspike.net>
* <Craig@chatspike.net>
*
* Written by Craig Edwards, Craig McLure, and others.
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
*/
/* Written by Special (john@yarbbles.com) */
#include "inspircd.h"
#include "http.h"
/* $ModDesc: HTTP client service provider */
class URL
{
public:
std::string url;
std::string protocol, username, password, domain, request;
int port;
};
class HTTPSocket : public InspSocket
{
private:
InspIRCd *Server;
class ModuleHTTPClient *Mod;
HTTPClientRequest *req;
HTTPClientResponse *response;
URL url;
enum { HTTP_CLOSED, HTTP_REQSENT, HTTP_HEADERS, HTTP_DATA } status;
std::string data;
public:
HTTPSocket(InspIRCd *Instance, class ModuleHTTPClient *Mod);
virtual ~HTTPSocket();
virtual bool DoRequest(HTTPClientRequest *req);
virtual bool ParseURL(const std::string &url);
virtual void Connect(const std::string &ip);
virtual bool OnConnected();
virtual bool OnDataReady();
virtual void OnClose();
};
class HTTPResolver : public Resolver
{
private:
HTTPSocket *socket;
public:
HTTPResolver(HTTPSocket *socket, InspIRCd *Instance, const string &hostname) : Resolver(Instance, hostname, DNS_QUERY_FORWARD), socket(socket)
{
}
void OnLookupComplete(const string &result)
{
socket->Connect(result);
}
void OnError(ResolverError e, const string &errmsg)
{
delete socket;
}
};
typedef vector<HTTPSocket*> HTTPList;
class ModuleHTTPClient : public Module
{
public:
HTTPList sockets;
ModuleHTTPClient(InspIRCd *Me)
: Module::Module(Me)
{
}
virtual ~ModuleHTTPClient()
{
for (HTTPList::iterator i = sockets.begin(); i != sockets.end(); i++)
delete *i;
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnRequest] = 1;
}
char *OnRequest(Request *req)
{
HTTPClientRequest *httpreq = (HTTPClientRequest *) req->GetData();
HTTPSocket *sock = new HTTPSocket(ServerInstance, this);
sock->DoRequest(httpreq);
// No return value
return NULL;
}
void SendReply(Module *to, HTTPClientResponse *data)
{
Request req((char *) data, this, to);
req.Send();
}
};
HTTPSocket::HTTPSocket(InspIRCd *Instance, ModuleHTTPClient *Mod)
: InspSocket(Instance), Server(Instance), Mod(Mod), status(HTTP_CLOSED)
{
this->ClosePending = false;
this->port = 80;
}
HTTPSocket::~HTTPSocket()
{
Close();
for (HTTPList::iterator i = Mod->sockets.begin(); i != Mod->sockets.end(); i++)
{
if (*i == this)
{
Mod->sockets.erase(i);
break;
}
}
}
bool HTTPSocket::DoRequest(HTTPClientRequest *req)
{
this->req = req;
if (!ParseURL(req->GetURL()))
return false;
this->port = url.port;
strlcpy(this->host, url.domain.c_str(), MAXBUF);
if (!inet_aton(this->host, &this->addy))
{
new HTTPResolver(this, Server, url.domain);
return true;
}
else
{
this->Connect(url.domain);
}
return true;
}
bool HTTPSocket::ParseURL(const std::string &iurl)
{
url.url = iurl;
url.port = 80;
// Tokenize by slashes (protocol:, blank, domain, request..)
int pos = 0, pstart = 0, pend = 0;
for (; ; pend = url.url.find('/', pstart))
{
string part = url.url.substr(pstart, pend);
switch (pos)
{
case 0:
// Protocol
if (part[part.length()-1] != ':')
return false;
url.protocol = part.substr(0, part.length() - 1);
break;
case 1:
// Empty, skip
break;
case 2:
// User and password (user:pass@)
string::size_type aend = part.find('@', 0);
if (aend != string::npos)
{
// Technically, it is valid to not have a password (username@domain)
string::size_type usrend = part.find(':', 0);
if ((usrend != string::npos) && (usrend < aend))
url.password = part.substr(usrend + 1, aend);
else
usrend = aend;
url.username = part.substr(0, usrend);
}
else
aend = 0;
// Port (:port)
string::size_type dend = part.find(':', aend);
if (dend != string::npos)
url.port = atoi(part.substr(dend + 1).c_str());
// Domain
url.domain = part.substr(aend + 1, dend);
// The rest of the string is the request
url.request = url.url.substr(pend);
break;
}
if (pos++ == 2)
break;
pstart = pend + 1;
}
return true;
}
void HTTPSocket::Connect(const string &ip)
{
strlcpy(this->IP, ip.c_str(), MAXBUF);
if (!this->DoConnect())
{
Server->Log(DEBUG, "Unable to connect HTTPSocket to %s", this->host);
delete this;
}
}
bool HTTPSocket::OnConnected()
{
std::string request = "GET " + url.request + " HTTP/1.1\r\n";
// Dump headers into the request
HeaderMap headers = req->GetHeaders();
for (HeaderMap::iterator i = headers.begin(); i != headers.end(); i++)
request += i->first + ": " + i->second + "\r\n";
// The Host header is required for HTTP 1.1 and isn't known when the request is created; if they didn't overload it
// manually, add it here
if (headers.find("Host") == headers.end())
request += "Host: " + url.domain + "\r\n";
request += "\r\n";
this->status = HTTP_REQSENT;
return this->Write(request);
}
bool HTTPSocket::OnDataReady()
{
char *data = this->Read();
if (!data)
{
this->Close();
return false;
}
// Needs buffering for incomplete reads..
char *lend;
if (this->status < HTTP_DATA)
{
while ((lend = strstr(data, "\r\n")) != NULL)
{
if (strncmp(data, "\r\n", 2) == 0)
{
this->status = HTTP_DATA;
break;
}
*lend = '\0';
if (this->status == HTTP_REQSENT)
{
// HTTP reply (HTTP/1.1 200 msg)
data += 9;
response = new HTTPClientResponse(url.url, atoi(data), data + 4);
this->status = HTTP_HEADERS;
continue;
}
char *hdata = strchr(data, ':');
if (!hdata)
continue;
*hdata = '\0';
response->AddHeader(data, hdata + 2);
data = lend + 2;
}
}
this->data += data;
return true;
}
void HTTPSocket::OnClose()
{
if (!data.length())
{
Server->Log(DEBUG, "HTTP socket closed unexpectedly (no content recieved)");
return;
}
Server->Log(DEBUG, "Got file from HTTP successfully");
response->data = data;
Mod->SendReply(req->GetSrc(), response);
}
class ModuleHTTPClientFactory : public ModuleFactory
{
public:
ModuleHTTPClientFactory()
{
}
~ModuleHTTPClientFactory()
{
}
Module *CreateModule(InspIRCd* Me)
{
return new ModuleHTTPClient(Me);
}
};
extern "C" void *init_module(void)
{
return new ModuleHTTPClientFactory;
}