From 7943704cfe4e7d6c853ddca82f2d0f3365fb2c88 Mon Sep 17 00:00:00 2001 From: "milo24x7@gmail.com" Date: Sun, 7 Jul 2013 21:40:05 +0000 Subject: Updated open source license declaration and fixed some formatting issues. --- NetEx/HttpClient.cpp | 158 ++-- NetEx/HttpClient.h | 108 ++- NetEx/HttpServer.cpp | 2030 +++++++++++++++++++++++---------------------- NetEx/HttpServer.h | 456 +++++----- NetEx/HttpServlet.cpp | 470 ++++++----- NetEx/HttpServlet.h | 192 +++-- NetEx/HttpServletExec.cpp | 489 +++++------ NetEx/HttpServletExec.h | 136 +-- NetEx/NetAddr.cpp | 238 +++--- NetEx/NetAddr.h | 138 +-- NetEx/NetClient.cpp | 256 +++--- NetEx/NetClient.h | 228 ++--- NetEx/NetGram.cpp | 246 +++--- NetEx/NetGram.h | 182 ++-- NetEx/NetHost.cpp | 228 ++--- NetEx/NetHost.h | 122 +-- NetEx/NetLayer.cpp | 210 ++--- NetEx/NetLayer.h | 112 ++- NetEx/NetLink.cpp | 998 +++++++++++----------- NetEx/NetLink.h | 248 +++--- NetEx/NetMsg.cpp | 202 +++-- NetEx/NetMsg.h | 186 +++-- NetEx/NetPeer.cpp | 904 ++++++++++---------- NetEx/NetPeer.h | 218 ++--- NetEx/NetServer.cpp | 544 ++++++------ NetEx/NetServer.h | 152 ++-- NetEx/NetSock.cpp | 708 ++++++++-------- NetEx/NetSock.h | 168 ++-- 28 files changed, 5499 insertions(+), 4828 deletions(-) (limited to 'NetEx') diff --git a/NetEx/HttpClient.cpp b/NetEx/HttpClient.cpp index 3f7e204..a9cef50 100644 --- a/NetEx/HttpClient.cpp +++ b/NetEx/HttpClient.cpp @@ -1,68 +1,92 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpClient.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "HttpClient.h" -#include "NetHost.h" -#include "NetLayer.h" -#include - -// +-------------------------------------------------------------------+ - -HttpClient::HttpClient(const NetAddr& server_addr) - : NetClient(server_addr) -{ -} - -HttpClient::~HttpClient() -{ - cookies.destroy(); -} - -HttpResponse* -HttpClient::DoRequest(HttpRequest& request) -{ - // add existing cookies to request before sending: - CombineCookies(request.GetCookies(), cookies); - - Text req = request.operator Text(); - Text msg = SendRecv(req); - HttpResponse* response = new(__FILE__,__LINE__) HttpResponse(msg); - - if (response) { - // save cookies returned in response: - CombineCookies(cookies, response->GetCookies()); - } - - return response; -} - -void -HttpClient::CombineCookies(List& dst, List& src) -{ - for (int i = 0; i < src.size(); i++) { - HttpParam* s = src[i]; - HttpParam* d = dst.find(s); - - if (d) { - d->value = s->value; - } - else { - HttpParam* cookie = new(__FILE__,__LINE__) HttpParam(s->name, s->value); - if (cookie) - dst.append(cookie); - } - } +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpClient.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "HttpClient.h" +#include "NetHost.h" +#include "NetLayer.h" +#include + +// +-------------------------------------------------------------------+ + +HttpClient::HttpClient(const NetAddr& server_addr) + : NetClient(server_addr) +{ +} + +HttpClient::~HttpClient() +{ + cookies.destroy(); +} + +HttpResponse* +HttpClient::DoRequest(HttpRequest& request) +{ + // add existing cookies to request before sending: + CombineCookies(request.GetCookies(), cookies); + + Text req = request.operator Text(); + Text msg = SendRecv(req); + HttpResponse* response = new(__FILE__,__LINE__) HttpResponse(msg); + + if (response) { + // save cookies returned in response: + CombineCookies(cookies, response->GetCookies()); + } + + return response; +} + +void +HttpClient::CombineCookies(List& dst, List& src) +{ + for (int i = 0; i < src.size(); i++) { + HttpParam* s = src[i]; + HttpParam* d = dst.find(s); + + if (d) { + d->value = s->value; + } + else { + HttpParam* cookie = new(__FILE__,__LINE__) HttpParam(s->name, s->value); + if (cookie) + dst.append(cookie); + } + } } \ No newline at end of file diff --git a/NetEx/HttpClient.h b/NetEx/HttpClient.h index 51ea161..e9b84b9 100644 --- a/NetEx/HttpClient.h +++ b/NetEx/HttpClient.h @@ -1,43 +1,67 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpClient.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - HTTP/1.1 client class -*/ - - -#ifndef HttpClient_h -#define HttpClient_h - -#include "NetClient.h" -#include "HttpServer.h" - -// +-------------------------------------------------------------------+ - -class HttpClient : public NetClient -{ -public: - static const char* TYPENAME() { return "HttpClient"; } - - HttpClient(const NetAddr& server_addr); - virtual ~HttpClient(); - - int operator == (const HttpClient& c) const { return this == &c; } - - HttpResponse* DoRequest(HttpRequest& request); - -protected: - void CombineCookies(List& dst, List& src); - - List cookies; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpClient.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + HTTP/1.1 client class +*/ + + +#ifndef HttpClient_h +#define HttpClient_h + +#include "NetClient.h" +#include "HttpServer.h" + +// +-------------------------------------------------------------------+ + +class HttpClient : public NetClient +{ +public: + static const char* TYPENAME() { return "HttpClient"; } + + HttpClient(const NetAddr& server_addr); + virtual ~HttpClient(); + + int operator == (const HttpClient& c) const { return this == &c; } + + HttpResponse* DoRequest(HttpRequest& request); + +protected: + void CombineCookies(List& dst, List& src); + + List cookies; +}; + + #endif HttpClient_h \ No newline at end of file diff --git a/NetEx/HttpServer.cpp b/NetEx/HttpServer.cpp index 5926a25..92a13b2 100644 --- a/NetEx/HttpServer.cpp +++ b/NetEx/HttpServer.cpp @@ -1,1003 +1,1027 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServer.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "HttpServer.h" -#include "NetLayer.h" - -#include -#include -#include - -// +-------------------------------------------------------------------+ - -HttpServer::HttpServer(WORD port, int poolsize) - : NetServer(port, poolsize) -{ - http_server_name = "Generic HttpServer 1.0"; -} - -HttpServer::~HttpServer() -{ } - -// +--------------------------------------------------------------------+ - -Text -HttpServer::ProcessRequest(Text msg, const NetAddr& addr) -{ - HttpRequest request(msg); - HttpResponse response; - - request.SetClientAddr(addr); - - switch (request.Method()) { - case HttpRequest::HTTP_GET: - if (DoGet(request, response)) - return response; - - case HttpRequest::HTTP_POST: - if (DoPost(request, response)) - return response; - - case HttpRequest::HTTP_HEAD: - if (DoHead(request, response)) - return response; - } - - return ErrorResponse(); -} - -// +--------------------------------------------------------------------+ - -Text -HttpServer::GetServerName() -{ - return http_server_name; -} - -void -HttpServer::SetServerName(const char* name) -{ - http_server_name = name; -} - -// +--------------------------------------------------------------------+ - -Text -HttpServer::DefaultResponse() -{ - Text response = "HTTP/1.1 200 OK\nServer: "; - response += http_server_name; - response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n"; - - return response; -} - -Text -HttpServer::ErrorResponse() -{ - Text response = "HTTP/1.1 500 Internal Server Error\nServer:"; - response += http_server_name; - response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n"; - - response += ""; - response += http_server_name; - response += " Error\n"; - response += "\n

"; - response += http_server_name; - response += "

\n

Veruca... sweetheart... angel... I'm not a magician!\n"; - response += "\n\n"; - - return response; -} - -// +--------------------------------------------------------------------+ - -bool -HttpServer::DoGet(HttpRequest& request, HttpResponse& response) -{ - char buffer[1024]; - Text content; - - content = ""; - content += http_server_name; - content += "\n"; - content += "\n

"; - content += http_server_name; - content += "

\n"; - content += "

Client Address:

\n"; - - sprintf_s(buffer, "%d.%d.%d.%d:%d

\n", - client_addr.B1(), - client_addr.B2(), - client_addr.B3(), - client_addr.B4(), - client_addr.Port()); - - content += buffer; - content += "

Request Method:

\n"; - - switch (request.Method()) { - case HttpRequest::HTTP_GET: - content += "GET"; - break; - - case HttpRequest::HTTP_POST: - content += "POST"; - break; - - case HttpRequest::HTTP_HEAD: - content += "HEAD"; - break; - - default: - content += "(unsupported?)"; - break; - } - - content += "
\n"; - content += "

URI Requested:

\n"; - content += request.URI(); - content += "
\n"; - - if (request.GetQuery().size() > 0) { - content += "

Query Parameters:

\n"; - - ListIter q_iter = request.GetQuery(); - while (++q_iter) { - HttpParam* q = q_iter.value(); - sprintf_s(buffer, "%s: %s
\n", q->name.data(), q->value.data()); - content += buffer; - } - } - - content += "

Request Headers:

\n"; - ListIter h_iter = request.GetHeaders(); - while (++h_iter) { - HttpParam* h = h_iter.value(); - sprintf_s(buffer, "%s: %s
\n", h->name.data(), h->value.data()); - content += buffer; - } - - content += "\n\n"; - - response.SetStatus(HttpResponse::SC_OK); - response.AddHeader("Server", http_server_name); - response.AddHeader("MIME-Version", "1.0"); - response.AddHeader("Content-Type", "text/html"); - response.SetContent(content); - - return true; -} - -// +--------------------------------------------------------------------+ - -bool -HttpServer::DoPost(HttpRequest& request, HttpResponse& response) -{ - return DoGet(request, response); -} - -// +--------------------------------------------------------------------+ - -bool -HttpServer::DoHead(HttpRequest& request, HttpResponse& response) -{ - if (DoGet(request, response)) { - int len = response.Content().length(); - - char buffer[256]; - sprintf_s(buffer, "%d", len); - response.SetHeader("Content-Length", buffer); - response.SetContent(""); - - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ - -HttpRequest::HttpRequest(const char* r) - : method(0) -{ - if (r && *r) - ParseRequest(r); -} - -HttpRequest::~HttpRequest() -{ - query.destroy(); - headers.destroy(); - cookies.destroy(); -} - -// +--------------------------------------------------------------------+ - -void -HttpRequest::ParseRequest(Text request) -{ - if (request.length() <= 8) - return; - - const char* pReq = 0; - const char* pURI = 0; - const char* pQuery = 0; - - switch (request[0]) { - case 'G': - if (request.indexOf("GET") == 0) - method = HTTP_GET; - break; - - case 'P': - if (request.indexOf("POST") == 0) - method = HTTP_POST; - break; - - case 'H': - if (request.indexOf("HEAD") == 0) - method = HTTP_HEAD; - break; - - default: - break; - } - - if (!method) return; - - char buffer[1024]; - int i = 0; - - // save the request line: - pReq = request.data(); - while (*pReq && *pReq != '\n') - buffer[i++] = *pReq++; - buffer[i] = 0; - - request_line = buffer; - i = 0; - - // find the URI: - pURI = request.data(); - while (*pURI && !isspace(*pURI)) - pURI++; - - while (*pURI && isspace(*pURI)) - pURI++; - - // copy the URI and find the query string: - while (*pURI && *pURI != '?' && !isspace(*pURI)) { - buffer[i++] = *pURI++; - } - - buffer[i] = 0; - uri = buffer; - pQuery = pURI; - - // parse the query string: - if (*pQuery == '?') { - pQuery++; - - while (*pQuery && !isspace(*pQuery)) { - char name_buf[1024]; - char value_buf[1024]; - - i = 0; - while (*pQuery && *pQuery != '=' && !isspace(*pQuery)) - name_buf[i++] = *pQuery++; - name_buf[i] = 0; - - if (*pQuery == '=') - pQuery++; - - i = 0; - while (*pQuery && *pQuery != '&' && !isspace(*pQuery)) - value_buf[i++] = *pQuery++; - value_buf[i] = 0; - - if (*pQuery == '&') - pQuery++; - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf)); - if (param) - query.append(param); - } - } - - // get the headers: - const char* p = request.data(); - while (*p && *p != '\n') - p++; - - if (*p == '\n') p++; - - while (*p && *p != '\r' && *p != '\n') { - char name_buf[1024]; - char value_buf[1024]; - - i = 0; - while (*p && *p != ':') - name_buf[i++] = *p++; - name_buf[i] = 0; - - p++; // skip ':' - while (isspace(*p)) p++; // skip spaces - - i = 0; - while (*p && *p != '\r' && *p != '\n') // read to end of header line - value_buf[i++] = *p++; - value_buf[i] = 0; - - if (!_stricmp(name_buf, "Cookie")) { - ParseCookie(value_buf); - } - else { - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf); - if (param) - headers.append(param); - } - - while (*p && *p != '\n') - p++; - - if (*p == '\n') p++; - } - - if (method == HTTP_POST && *p) { - while (*p == '\n' || *p == '\r') - p++; - - content = *p; - pQuery = p; - - while (*pQuery && !isspace(*pQuery)) { - char name_buf[1024]; - char value_buf[1024]; - - i = 0; - while (*pQuery && *pQuery != '=' && !isspace(*pQuery)) - name_buf[i++] = *pQuery++; - name_buf[i] = 0; - - if (*pQuery == '=') - pQuery++; - - i = 0; - while (*pQuery && *pQuery != '&' && !isspace(*pQuery)) - value_buf[i++] = *pQuery++; - value_buf[i] = 0; - - if (*pQuery == '&') - pQuery++; - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf)); - if (param) - query.append(param); - } - } -} - -void -HttpRequest::ParseCookie(const char* param) -{ - const char* p = param; - - while (p && *p) { - while (isspace(*p)) p++; - - // just ignore reserved attributes - if (*p == '$') { - while (*p && !isspace(*p) && *p != ';') p++; - - if (*p == ';') - p++; - } - - // found a cookie! - else if (isalpha(*p)) { - char name[1024]; - char data[1024]; - - char* d = name; - while (*p && *p != '=') - *d++ = *p++; - *d = 0; - - if (*p == '=') - p++; - - if (*p == '"') - p++; - - d = data; - while (*p && *p != '"' && *p != ';') - *d++ = *p++; - *d = 0; - - if (*p == '"') - p++; - - if (*p == ';') - p++; - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data); - if (param) - cookies.append(param); - } - - // this shouldn't happen - abandon the parse - else { - return; - } - } -} - -// +--------------------------------------------------------------------+ - -Text -HttpRequest::GetParam(const char* name) -{ - ListIter iter = query; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -// +--------------------------------------------------------------------+ - -Text -HttpRequest::GetHeader(const char* name) -{ - ListIter iter = headers; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -void -HttpRequest::SetHeader(const char* name, const char* value) -{ - ListIter iter = headers; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = value; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - headers.append(param); -} - -void -HttpRequest::AddHeader(const char* name, const char* value) -{ - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - headers.append(param); -} - -// +--------------------------------------------------------------------+ - -Text -HttpRequest::GetCookie(const char* name) -{ - ListIter iter = cookies; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -void -HttpRequest::SetCookie(const char* name, const char* value) -{ - ListIter iter = cookies; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = value; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - cookies.append(param); -} - -void -HttpRequest::AddCookie(const char* name, const char* value) -{ - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - cookies.append(param); -} - -// +--------------------------------------------------------------------+ - -Text -HttpRequest::DecodeParam(const char* value) -{ - if (!value || !*value) return ""; - - int size = strlen(value); - char val = 0; - char code[4]; - char sbuf[256]; - char* lbuf = 0; - - char* dst = sbuf; - char* p = sbuf; - - if (size > 255) { - lbuf = new(__FILE__,__LINE__) char[size+1]; - dst = lbuf; - p = lbuf; - } - - if (p) { - while (*value) { - switch (*value) { - default: *p++ = *value; break; - case '+': *p++ = ' '; break; - - case '%': - value++; - code[0] = *value++; - code[1] = *value; - code[2] = 0; - - val = (char) strtol(code, 0, 16); - *p++ = val; - break; - } - - value++; - } - - *p = 0; - } - - Text result = dst; - - if (lbuf) - delete [] lbuf; - - return result; -} - -// +--------------------------------------------------------------------+ - -Text -HttpRequest::EncodeParam(const char* value) -{ - if (!value || !*value) return ""; - - int size = strlen(value); - char hex1 = 0; - char hex2 = 0; - - char sbuf[1024]; - char* lbuf = 0; - - char* dst = sbuf; - char* p = sbuf; - - if (size > 255) { - lbuf = new(__FILE__,__LINE__) char[4*size+1]; - dst = lbuf; - p = lbuf; - } - - if (p) { - while (*value) { - switch (*value) { - default: *p++ = *value; break; - case ' ': *p++ = '+'; break; - - case '?': *p++ = '%'; *p++ = '3'; *p++ = 'F'; break; - case '&': *p++ = '%'; *p++ = '2'; *p++ = '6'; break; - case ':': *p++ = '%'; *p++ = '3'; *p++ = 'A'; break; - case '/': *p++ = '%'; *p++ = '2'; *p++ = 'F'; break; - case '\\': *p++ = '%'; *p++ = '5'; *p++ = 'C'; break; - case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break; - case '|': *p++ = '%'; *p++ = '7'; *p++ = 'C'; break; - case '<': *p++ = '%'; *p++ = '3'; *p++ = 'C'; break; - case '>': *p++ = '%'; *p++ = '3'; *p++ = 'E'; break; - case '[': *p++ = '%'; *p++ = '5'; *p++ = 'B'; break; - case ']': *p++ = '%'; *p++ = '5'; *p++ = 'D'; break; - case '{': *p++ = '%'; *p++ = '7'; *p++ = 'B'; break; - case '}': *p++ = '%'; *p++ = '7'; *p++ = 'D'; break; - case '"': *p++ = '%'; *p++ = '2'; *p++ = '2'; break; - case '^': *p++ = '%'; *p++ = '5'; *p++ = 'E'; break; - case '`': *p++ = '%'; *p++ = '6'; *p++ = '0'; break; - case '\n': break; - case '\r': break; - case '\t': break; - } - - value++; - } - - *p = 0; - } - - Text result = dst; - - if (lbuf) - delete [] lbuf; - - return result; -} - -// +--------------------------------------------------------------------+ - -HttpRequest::operator Text() -{ - Text response = request_line.data(); - response += "\n"; - - for (int i = 0; i < headers.size(); i++) { - HttpParam* h = headers[i]; - response += h->name; - response += ": "; - response += h->value; - response += "\n"; - } - - for (int i = 0; i < cookies.size(); i++) { - HttpParam* c = cookies[i]; - response += "Cookie: "; - response += c->name; - response += "=\""; - response += c->value; - response += "\"\n"; - } - - response += "Connection: close\n\n"; - response += content; - - return response; -} - -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ - -HttpResponse::HttpResponse(int stat, const char* data) - : status(stat), content(data) -{ } - -HttpResponse::HttpResponse(const char* r) - : status(0), content(r) -{ - if (r && *r) - ParseResponse(r); -} - -HttpResponse::~HttpResponse() -{ - headers.destroy(); - cookies.destroy(); -} - -// +--------------------------------------------------------------------+ - -HttpResponse::operator Text() -{ - Text response; - - switch (status) { - case SC_CONTINUE : response = "HTTP/1.1 100 Continue\n"; break; - case SC_SWITCHING_PROTOCOLS : response = "HTTP/1.1 101 Switching Protocols\n"; break; - - case SC_OK : response = "HTTP/1.1 200 OK\n"; break; - case SC_CREATED : response = "HTTP/1.1 201 Created\n"; break; - case SC_ACCEPTED : response = "HTTP/1.1 202 Accepted\n"; break; - case SC_NON_AUTHORITATIVE : response = "HTTP/1.1 203 Non Authoritative\n"; break; - case SC_NO_CONTENT : response = "HTTP/1.1 204 No Content\n"; break; - case SC_RESET_CONTENT : response = "HTTP/1.1 205 Reset Content\n"; break; - case SC_PARTIAL_CONTENT : response = "HTTP/1.1 206 Partial Content\n"; break; - - case SC_MULTIPLE_CHOICES : response = "HTTP/1.1 300 Multiple Choices\n"; break; - case SC_MOVED_PERMANENTLY : response = "HTTP/1.1 301 Moved Permanently\n"; break; - case SC_FOUND : response = "HTTP/1.1 302 Found\n"; break; - case SC_SEE_OTHER : response = "HTTP/1.1 303 See Other\n"; break; - case SC_NOT_MODIFIED : response = "HTTP/1.1 304 Not Modified\n"; break; - case SC_USE_PROXY : response = "HTTP/1.1 305 Use Proxy\n"; break; - case SC_TEMPORARY_REDIRECT : response = "HTTP/1.1 307 Temporary Redirect\n"; break; - - case SC_BAD_REQUEST : response = "HTTP/1.1 400 Bad Request\n"; break; - case SC_UNAUTHORIZED : response = "HTTP/1.1 401 Unauthorized\n"; break; - case SC_PAYMENT_REQUIRED : response = "HTTP/1.1 402 Payment Required\n"; break; - case SC_FORBIDDEN : response = "HTTP/1.1 403 Forbidden\n"; break; - case SC_NOT_FOUND : response = "HTTP/1.1 404 Not Found\n"; break; - case SC_METHOD_NOT_ALLOWED : response = "HTTP/1.1 405 Method Not Allowed\n"; break; - case SC_NOT_ACCEPTABLE : response = "HTTP/1.1 406 Not Acceptable\n"; break; - case SC_PROXY_AUTH_REQ : response = "HTTP/1.1 407 Proxy Authorization Req\n"; break; - case SC_REQUEST_TIME_OUT : response = "HTTP/1.1 408 Request Timeout\n"; break; - case SC_CONFLICT : response = "HTTP/1.1 409 Conflict\n"; break; - case SC_GONE : response = "HTTP/1.1 410 Gone\n"; break; - case SC_LENGTH_REQUIRED : response = "HTTP/1.1 411 Length Required\n"; break; - - default: - case SC_SERVER_ERROR : response = "HTTP/1.1 500 Internal Server Error\n"; break; - case SC_NOT_IMPLEMENTED : response = "HTTP/1.1 501 Not Implemented\n"; break; - case SC_BAD_GATEWAY : response = "HTTP/1.1 502 Bad Gateway\n"; break; - case SC_SERVICE_UNAVAILABLE : response = "HTTP/1.1 503 Service Unavailable\n"; break; - case SC_GATEWAY_TIMEOUT : response = "HTTP/1.1 504 Gateway Timeout\n"; break; - case SC_VERSION_NOT_SUPPORTED: response = "HTTP/1.1 505 HTTP Version Not Supported\n"; break; - } - - SetHeader("Connection", "close"); - - char buffer[256]; - - if (content.length()) { - sprintf_s(buffer, "%d", content.length()); - SetHeader("Content-Length", buffer); - } - - for (int i = 0; i < cookies.size(); i++) { - HttpParam* cookie = cookies.at(i); - sprintf_s(buffer, "%s=\"%s\"; Version=\"1\"", cookie->name.data(), cookie->value.data()); - - AddHeader("Set-Cookie", buffer); - } - - for (int i = 0; i < headers.size(); i++) { - const HttpParam* p = headers.at(i); - sprintf_s(buffer, "%s: %s\n", p->name.data(), p->value.data()); - response += buffer; - } - - response += "\n"; - response += content; - - return response; -} - -// +--------------------------------------------------------------------+ - -Text -HttpResponse::GetHeader(const char* name) -{ - ListIter iter = headers; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -void -HttpResponse::SetHeader(const char* name, const char* value) -{ - ListIter iter = headers; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = value; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - headers.append(param); -} - -void -HttpResponse::AddHeader(const char* name, const char* value) -{ - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - headers.append(param); -} - -// +--------------------------------------------------------------------+ - -Text -HttpResponse::GetCookie(const char* name) -{ - ListIter iter = cookies; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -void -HttpResponse::SetCookie(const char* name, const char* value) -{ - ListIter iter = cookies; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = value; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - cookies.append(param); -} - -void -HttpResponse::AddCookie(const char* name, const char* value) -{ - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - cookies.append(param); -} - -// +--------------------------------------------------------------------+ - -void -HttpResponse::SendRedirect(const char* url) -{ - status = SC_TEMPORARY_REDIRECT; - SetHeader("Location", url); -} - -// +--------------------------------------------------------------------+ - -void -HttpResponse::ParseResponse(Text response) -{ - if (response.length() <= 12 || response.indexOf("HTTP/1.") != 0) - return; - - const char* pStatus = response.data() + 9; - - sscanf_s(pStatus, "%d", &status); - if (!status) return; - - int i = 0; - - // get the headers: - const char* p = response.data(); - while (*p && *p != '\n') - p++; - - if (*p == '\n') p++; - - while (*p && *p != '\r' && *p != '\n') { - char name_buf[1024]; - char value_buf[1024]; - - i = 0; - while (*p && *p != ':') - name_buf[i++] = *p++; - name_buf[i] = 0; - - p++; // skip ':' - while (isspace(*p)) p++; // skip spaces - - i = 0; - while (*p && *p != '\r' && *p != '\n') // read to end of header line - value_buf[i++] = *p++; - value_buf[i] = 0; - - if (!_stricmp(name_buf, "Set-Cookie")) { - ParseCookie(value_buf); - } - else { - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf); - if (param) - headers.append(param); - } - - while (*p && *p != '\n') - p++; - - if (*p == '\n') p++; - } - - if (*p == '\n') p++; - content = p; -} - -void -HttpResponse::ParseCookie(const char* param) -{ - const char* p = param; - - while (p && *p) { - while (isspace(*p)) p++; - - // just ignore reserved attributes - if (*p == '$') { - while (*p && !isspace(*p) && *p != ';') p++; - - if (*p == ';') - p++; - } - - // found a cookie! - else if (isalpha(*p)) { - char name[1024]; - char data[1024]; - - char* d = name; - while (*p && *p != '=') - *d++ = *p++; - *d = 0; - - if (*p == '=') - p++; - - if (*p == '"') - p++; - - d = data; - while (*p && *p != '"' && *p != ';') - *d++ = *p++; - *d = 0; - - if (*p == '"') - p++; - - if (*p == ';') - p++; - - // ignore the version attribute - if (_stricmp(name, "version")) { - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data); - if (param) - cookies.append(param); - } - } - - // this shouldn't happen - abandon the parse - else { - return; - } - } -} - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServer.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "HttpServer.h" +#include "NetLayer.h" + +#include +#include +#include + +// +-------------------------------------------------------------------+ + +HttpServer::HttpServer(WORD port, int poolsize) + : NetServer(port, poolsize) +{ + http_server_name = "Generic HttpServer 1.0"; +} + +HttpServer::~HttpServer() +{ } + +// +--------------------------------------------------------------------+ + +Text +HttpServer::ProcessRequest(Text msg, const NetAddr& addr) +{ + HttpRequest request(msg); + HttpResponse response; + + request.SetClientAddr(addr); + + switch (request.Method()) { + case HttpRequest::HTTP_GET: + if (DoGet(request, response)) + return response; + + case HttpRequest::HTTP_POST: + if (DoPost(request, response)) + return response; + + case HttpRequest::HTTP_HEAD: + if (DoHead(request, response)) + return response; + } + + return ErrorResponse(); +} + +// +--------------------------------------------------------------------+ + +Text +HttpServer::GetServerName() +{ + return http_server_name; +} + +void +HttpServer::SetServerName(const char* name) +{ + http_server_name = name; +} + +// +--------------------------------------------------------------------+ + +Text +HttpServer::DefaultResponse() +{ + Text response = "HTTP/1.1 200 OK\nServer: "; + response += http_server_name; + response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n"; + + return response; +} + +Text +HttpServer::ErrorResponse() +{ + Text response = "HTTP/1.1 500 Internal Server Error\nServer:"; + response += http_server_name; + response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n"; + + response += ""; + response += http_server_name; + response += " Error\n"; + response += "\n

"; + response += http_server_name; + response += "

\n

Veruca... sweetheart... angel... I'm not a magician!\n"; + response += "\n\n"; + + return response; +} + +// +--------------------------------------------------------------------+ + +bool +HttpServer::DoGet(HttpRequest& request, HttpResponse& response) +{ + char buffer[1024]; + Text content; + + content = ""; + content += http_server_name; + content += "\n"; + content += "\n

"; + content += http_server_name; + content += "

\n"; + content += "

Client Address:

\n"; + + sprintf_s(buffer, "%d.%d.%d.%d:%d

\n", + client_addr.B1(), + client_addr.B2(), + client_addr.B3(), + client_addr.B4(), + client_addr.Port()); + + content += buffer; + content += "

Request Method:

\n"; + + switch (request.Method()) { + case HttpRequest::HTTP_GET: + content += "GET"; + break; + + case HttpRequest::HTTP_POST: + content += "POST"; + break; + + case HttpRequest::HTTP_HEAD: + content += "HEAD"; + break; + + default: + content += "(unsupported?)"; + break; + } + + content += "
\n"; + content += "

URI Requested:

\n"; + content += request.URI(); + content += "
\n"; + + if (request.GetQuery().size() > 0) { + content += "

Query Parameters:

\n"; + + ListIter q_iter = request.GetQuery(); + while (++q_iter) { + HttpParam* q = q_iter.value(); + sprintf_s(buffer, "%s: %s
\n", q->name.data(), q->value.data()); + content += buffer; + } + } + + content += "

Request Headers:

\n"; + ListIter h_iter = request.GetHeaders(); + while (++h_iter) { + HttpParam* h = h_iter.value(); + sprintf_s(buffer, "%s: %s
\n", h->name.data(), h->value.data()); + content += buffer; + } + + content += "\n\n"; + + response.SetStatus(HttpResponse::SC_OK); + response.AddHeader("Server", http_server_name); + response.AddHeader("MIME-Version", "1.0"); + response.AddHeader("Content-Type", "text/html"); + response.SetContent(content); + + return true; +} + +// +--------------------------------------------------------------------+ + +bool +HttpServer::DoPost(HttpRequest& request, HttpResponse& response) +{ + return DoGet(request, response); +} + +// +--------------------------------------------------------------------+ + +bool +HttpServer::DoHead(HttpRequest& request, HttpResponse& response) +{ + if (DoGet(request, response)) { + int len = response.Content().length(); + + char buffer[256]; + sprintf_s(buffer, "%d", len); + response.SetHeader("Content-Length", buffer); + response.SetContent(""); + + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ + +HttpRequest::HttpRequest(const char* r) + : method(0) +{ + if (r && *r) + ParseRequest(r); +} + +HttpRequest::~HttpRequest() +{ + query.destroy(); + headers.destroy(); + cookies.destroy(); +} + +// +--------------------------------------------------------------------+ + +void +HttpRequest::ParseRequest(Text request) +{ + if (request.length() <= 8) + return; + + const char* pReq = 0; + const char* pURI = 0; + const char* pQuery = 0; + + switch (request[0]) { + case 'G': + if (request.indexOf("GET") == 0) + method = HTTP_GET; + break; + + case 'P': + if (request.indexOf("POST") == 0) + method = HTTP_POST; + break; + + case 'H': + if (request.indexOf("HEAD") == 0) + method = HTTP_HEAD; + break; + + default: + break; + } + + if (!method) return; + + char buffer[1024]; + int i = 0; + + // save the request line: + pReq = request.data(); + while (*pReq && *pReq != '\n') + buffer[i++] = *pReq++; + buffer[i] = 0; + + request_line = buffer; + i = 0; + + // find the URI: + pURI = request.data(); + while (*pURI && !isspace(*pURI)) + pURI++; + + while (*pURI && isspace(*pURI)) + pURI++; + + // copy the URI and find the query string: + while (*pURI && *pURI != '?' && !isspace(*pURI)) { + buffer[i++] = *pURI++; + } + + buffer[i] = 0; + uri = buffer; + pQuery = pURI; + + // parse the query string: + if (*pQuery == '?') { + pQuery++; + + while (*pQuery && !isspace(*pQuery)) { + char name_buf[1024]; + char value_buf[1024]; + + i = 0; + while (*pQuery && *pQuery != '=' && !isspace(*pQuery)) + name_buf[i++] = *pQuery++; + name_buf[i] = 0; + + if (*pQuery == '=') + pQuery++; + + i = 0; + while (*pQuery && *pQuery != '&' && !isspace(*pQuery)) + value_buf[i++] = *pQuery++; + value_buf[i] = 0; + + if (*pQuery == '&') + pQuery++; + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf)); + if (param) + query.append(param); + } + } + + // get the headers: + const char* p = request.data(); + while (*p && *p != '\n') + p++; + + if (*p == '\n') p++; + + while (*p && *p != '\r' && *p != '\n') { + char name_buf[1024]; + char value_buf[1024]; + + i = 0; + while (*p && *p != ':') + name_buf[i++] = *p++; + name_buf[i] = 0; + + p++; // skip ':' + while (isspace(*p)) p++; // skip spaces + + i = 0; + while (*p && *p != '\r' && *p != '\n') // read to end of header line + value_buf[i++] = *p++; + value_buf[i] = 0; + + if (!_stricmp(name_buf, "Cookie")) { + ParseCookie(value_buf); + } + else { + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf); + if (param) + headers.append(param); + } + + while (*p && *p != '\n') + p++; + + if (*p == '\n') p++; + } + + if (method == HTTP_POST && *p) { + while (*p == '\n' || *p == '\r') + p++; + + content = *p; + pQuery = p; + + while (*pQuery && !isspace(*pQuery)) { + char name_buf[1024]; + char value_buf[1024]; + + i = 0; + while (*pQuery && *pQuery != '=' && !isspace(*pQuery)) + name_buf[i++] = *pQuery++; + name_buf[i] = 0; + + if (*pQuery == '=') + pQuery++; + + i = 0; + while (*pQuery && *pQuery != '&' && !isspace(*pQuery)) + value_buf[i++] = *pQuery++; + value_buf[i] = 0; + + if (*pQuery == '&') + pQuery++; + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf)); + if (param) + query.append(param); + } + } +} + +void +HttpRequest::ParseCookie(const char* param) +{ + const char* p = param; + + while (p && *p) { + while (isspace(*p)) p++; + + // just ignore reserved attributes + if (*p == '$') { + while (*p && !isspace(*p) && *p != ';') p++; + + if (*p == ';') + p++; + } + + // found a cookie! + else if (isalpha(*p)) { + char name[1024]; + char data[1024]; + + char* d = name; + while (*p && *p != '=') + *d++ = *p++; + *d = 0; + + if (*p == '=') + p++; + + if (*p == '"') + p++; + + d = data; + while (*p && *p != '"' && *p != ';') + *d++ = *p++; + *d = 0; + + if (*p == '"') + p++; + + if (*p == ';') + p++; + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data); + if (param) + cookies.append(param); + } + + // this shouldn't happen - abandon the parse + else { + return; + } + } +} + +// +--------------------------------------------------------------------+ + +Text +HttpRequest::GetParam(const char* name) +{ + ListIter iter = query; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +// +--------------------------------------------------------------------+ + +Text +HttpRequest::GetHeader(const char* name) +{ + ListIter iter = headers; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +void +HttpRequest::SetHeader(const char* name, const char* value) +{ + ListIter iter = headers; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = value; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + headers.append(param); +} + +void +HttpRequest::AddHeader(const char* name, const char* value) +{ + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + headers.append(param); +} + +// +--------------------------------------------------------------------+ + +Text +HttpRequest::GetCookie(const char* name) +{ + ListIter iter = cookies; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +void +HttpRequest::SetCookie(const char* name, const char* value) +{ + ListIter iter = cookies; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = value; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + cookies.append(param); +} + +void +HttpRequest::AddCookie(const char* name, const char* value) +{ + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + cookies.append(param); +} + +// +--------------------------------------------------------------------+ + +Text +HttpRequest::DecodeParam(const char* value) +{ + if (!value || !*value) return ""; + + int size = strlen(value); + char val = 0; + char code[4]; + char sbuf[256]; + char* lbuf = 0; + + char* dst = sbuf; + char* p = sbuf; + + if (size > 255) { + lbuf = new(__FILE__,__LINE__) char[size+1]; + dst = lbuf; + p = lbuf; + } + + if (p) { + while (*value) { + switch (*value) { + default: *p++ = *value; break; + case '+': *p++ = ' '; break; + + case '%': + value++; + code[0] = *value++; + code[1] = *value; + code[2] = 0; + + val = (char) strtol(code, 0, 16); + *p++ = val; + break; + } + + value++; + } + + *p = 0; + } + + Text result = dst; + + if (lbuf) + delete [] lbuf; + + return result; +} + +// +--------------------------------------------------------------------+ + +Text +HttpRequest::EncodeParam(const char* value) +{ + if (!value || !*value) return ""; + + int size = strlen(value); + char hex1 = 0; + char hex2 = 0; + + char sbuf[1024]; + char* lbuf = 0; + + char* dst = sbuf; + char* p = sbuf; + + if (size > 255) { + lbuf = new(__FILE__,__LINE__) char[4*size+1]; + dst = lbuf; + p = lbuf; + } + + if (p) { + while (*value) { + switch (*value) { + default: *p++ = *value; break; + case ' ': *p++ = '+'; break; + + case '?': *p++ = '%'; *p++ = '3'; *p++ = 'F'; break; + case '&': *p++ = '%'; *p++ = '2'; *p++ = '6'; break; + case ':': *p++ = '%'; *p++ = '3'; *p++ = 'A'; break; + case '/': *p++ = '%'; *p++ = '2'; *p++ = 'F'; break; + case '\\': *p++ = '%'; *p++ = '5'; *p++ = 'C'; break; + case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break; + case '|': *p++ = '%'; *p++ = '7'; *p++ = 'C'; break; + case '<': *p++ = '%'; *p++ = '3'; *p++ = 'C'; break; + case '>': *p++ = '%'; *p++ = '3'; *p++ = 'E'; break; + case '[': *p++ = '%'; *p++ = '5'; *p++ = 'B'; break; + case ']': *p++ = '%'; *p++ = '5'; *p++ = 'D'; break; + case '{': *p++ = '%'; *p++ = '7'; *p++ = 'B'; break; + case '}': *p++ = '%'; *p++ = '7'; *p++ = 'D'; break; + case '"': *p++ = '%'; *p++ = '2'; *p++ = '2'; break; + case '^': *p++ = '%'; *p++ = '5'; *p++ = 'E'; break; + case '`': *p++ = '%'; *p++ = '6'; *p++ = '0'; break; + case '\n': break; + case '\r': break; + case '\t': break; + } + + value++; + } + + *p = 0; + } + + Text result = dst; + + if (lbuf) + delete [] lbuf; + + return result; +} + +// +--------------------------------------------------------------------+ + +HttpRequest::operator Text() +{ + Text response = request_line.data(); + response += "\n"; + + for (int i = 0; i < headers.size(); i++) { + HttpParam* h = headers[i]; + response += h->name; + response += ": "; + response += h->value; + response += "\n"; + } + + for (int i = 0; i < cookies.size(); i++) { + HttpParam* c = cookies[i]; + response += "Cookie: "; + response += c->name; + response += "=\""; + response += c->value; + response += "\"\n"; + } + + response += "Connection: close\n\n"; + response += content; + + return response; +} + +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ + +HttpResponse::HttpResponse(int stat, const char* data) + : status(stat), content(data) +{ } + +HttpResponse::HttpResponse(const char* r) + : status(0), content(r) +{ + if (r && *r) + ParseResponse(r); +} + +HttpResponse::~HttpResponse() +{ + headers.destroy(); + cookies.destroy(); +} + +// +--------------------------------------------------------------------+ + +HttpResponse::operator Text() +{ + Text response; + + switch (status) { + case SC_CONTINUE : response = "HTTP/1.1 100 Continue\n"; break; + case SC_SWITCHING_PROTOCOLS : response = "HTTP/1.1 101 Switching Protocols\n"; break; + + case SC_OK : response = "HTTP/1.1 200 OK\n"; break; + case SC_CREATED : response = "HTTP/1.1 201 Created\n"; break; + case SC_ACCEPTED : response = "HTTP/1.1 202 Accepted\n"; break; + case SC_NON_AUTHORITATIVE : response = "HTTP/1.1 203 Non Authoritative\n"; break; + case SC_NO_CONTENT : response = "HTTP/1.1 204 No Content\n"; break; + case SC_RESET_CONTENT : response = "HTTP/1.1 205 Reset Content\n"; break; + case SC_PARTIAL_CONTENT : response = "HTTP/1.1 206 Partial Content\n"; break; + + case SC_MULTIPLE_CHOICES : response = "HTTP/1.1 300 Multiple Choices\n"; break; + case SC_MOVED_PERMANENTLY : response = "HTTP/1.1 301 Moved Permanently\n"; break; + case SC_FOUND : response = "HTTP/1.1 302 Found\n"; break; + case SC_SEE_OTHER : response = "HTTP/1.1 303 See Other\n"; break; + case SC_NOT_MODIFIED : response = "HTTP/1.1 304 Not Modified\n"; break; + case SC_USE_PROXY : response = "HTTP/1.1 305 Use Proxy\n"; break; + case SC_TEMPORARY_REDIRECT : response = "HTTP/1.1 307 Temporary Redirect\n"; break; + + case SC_BAD_REQUEST : response = "HTTP/1.1 400 Bad Request\n"; break; + case SC_UNAUTHORIZED : response = "HTTP/1.1 401 Unauthorized\n"; break; + case SC_PAYMENT_REQUIRED : response = "HTTP/1.1 402 Payment Required\n"; break; + case SC_FORBIDDEN : response = "HTTP/1.1 403 Forbidden\n"; break; + case SC_NOT_FOUND : response = "HTTP/1.1 404 Not Found\n"; break; + case SC_METHOD_NOT_ALLOWED : response = "HTTP/1.1 405 Method Not Allowed\n"; break; + case SC_NOT_ACCEPTABLE : response = "HTTP/1.1 406 Not Acceptable\n"; break; + case SC_PROXY_AUTH_REQ : response = "HTTP/1.1 407 Proxy Authorization Req\n"; break; + case SC_REQUEST_TIME_OUT : response = "HTTP/1.1 408 Request Timeout\n"; break; + case SC_CONFLICT : response = "HTTP/1.1 409 Conflict\n"; break; + case SC_GONE : response = "HTTP/1.1 410 Gone\n"; break; + case SC_LENGTH_REQUIRED : response = "HTTP/1.1 411 Length Required\n"; break; + + default: + case SC_SERVER_ERROR : response = "HTTP/1.1 500 Internal Server Error\n"; break; + case SC_NOT_IMPLEMENTED : response = "HTTP/1.1 501 Not Implemented\n"; break; + case SC_BAD_GATEWAY : response = "HTTP/1.1 502 Bad Gateway\n"; break; + case SC_SERVICE_UNAVAILABLE : response = "HTTP/1.1 503 Service Unavailable\n"; break; + case SC_GATEWAY_TIMEOUT : response = "HTTP/1.1 504 Gateway Timeout\n"; break; + case SC_VERSION_NOT_SUPPORTED: response = "HTTP/1.1 505 HTTP Version Not Supported\n"; break; + } + + SetHeader("Connection", "close"); + + char buffer[256]; + + if (content.length()) { + sprintf_s(buffer, "%d", content.length()); + SetHeader("Content-Length", buffer); + } + + for (int i = 0; i < cookies.size(); i++) { + HttpParam* cookie = cookies.at(i); + sprintf_s(buffer, "%s=\"%s\"; Version=\"1\"", cookie->name.data(), cookie->value.data()); + + AddHeader("Set-Cookie", buffer); + } + + for (int i = 0; i < headers.size(); i++) { + const HttpParam* p = headers.at(i); + sprintf_s(buffer, "%s: %s\n", p->name.data(), p->value.data()); + response += buffer; + } + + response += "\n"; + response += content; + + return response; +} + +// +--------------------------------------------------------------------+ + +Text +HttpResponse::GetHeader(const char* name) +{ + ListIter iter = headers; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +void +HttpResponse::SetHeader(const char* name, const char* value) +{ + ListIter iter = headers; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = value; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + headers.append(param); +} + +void +HttpResponse::AddHeader(const char* name, const char* value) +{ + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + headers.append(param); +} + +// +--------------------------------------------------------------------+ + +Text +HttpResponse::GetCookie(const char* name) +{ + ListIter iter = cookies; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +void +HttpResponse::SetCookie(const char* name, const char* value) +{ + ListIter iter = cookies; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = value; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + cookies.append(param); +} + +void +HttpResponse::AddCookie(const char* name, const char* value) +{ + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + cookies.append(param); +} + +// +--------------------------------------------------------------------+ + +void +HttpResponse::SendRedirect(const char* url) +{ + status = SC_TEMPORARY_REDIRECT; + SetHeader("Location", url); +} + +// +--------------------------------------------------------------------+ + +void +HttpResponse::ParseResponse(Text response) +{ + if (response.length() <= 12 || response.indexOf("HTTP/1.") != 0) + return; + + const char* pStatus = response.data() + 9; + + sscanf_s(pStatus, "%d", &status); + if (!status) return; + + int i = 0; + + // get the headers: + const char* p = response.data(); + while (*p && *p != '\n') + p++; + + if (*p == '\n') p++; + + while (*p && *p != '\r' && *p != '\n') { + char name_buf[1024]; + char value_buf[1024]; + + i = 0; + while (*p && *p != ':') + name_buf[i++] = *p++; + name_buf[i] = 0; + + p++; // skip ':' + while (isspace(*p)) p++; // skip spaces + + i = 0; + while (*p && *p != '\r' && *p != '\n') // read to end of header line + value_buf[i++] = *p++; + value_buf[i] = 0; + + if (!_stricmp(name_buf, "Set-Cookie")) { + ParseCookie(value_buf); + } + else { + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf); + if (param) + headers.append(param); + } + + while (*p && *p != '\n') + p++; + + if (*p == '\n') p++; + } + + if (*p == '\n') p++; + content = p; +} + +void +HttpResponse::ParseCookie(const char* param) +{ + const char* p = param; + + while (p && *p) { + while (isspace(*p)) p++; + + // just ignore reserved attributes + if (*p == '$') { + while (*p && !isspace(*p) && *p != ';') p++; + + if (*p == ';') + p++; + } + + // found a cookie! + else if (isalpha(*p)) { + char name[1024]; + char data[1024]; + + char* d = name; + while (*p && *p != '=') + *d++ = *p++; + *d = 0; + + if (*p == '=') + p++; + + if (*p == '"') + p++; + + d = data; + while (*p && *p != '"' && *p != ';') + *d++ = *p++; + *d = 0; + + if (*p == '"') + p++; + + if (*p == ';') + p++; + + // ignore the version attribute + if (_stricmp(name, "version")) { + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data); + if (param) + cookies.append(param); + } + } + + // this shouldn't happen - abandon the parse + else { + return; + } + } +} + diff --git a/NetEx/HttpServer.h b/NetEx/HttpServer.h index fe811b3..fb74fbc 100644 --- a/NetEx/HttpServer.h +++ b/NetEx/HttpServer.h @@ -1,217 +1,241 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServer.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#ifndef HttpServer_h -#define HttpServer_h - -#include "NetServer.h" - -// +-------------------------------------------------------------------+ - -class HttpParam; -class HttpRequest; -class HttpResponse; - -// +-------------------------------------------------------------------+ - -class HttpServer : public NetServer -{ -public: - static const char* TYPENAME() { return "HttpServer"; } - - HttpServer(WORD port, int poolsize=1); - virtual ~HttpServer(); - - int operator == (const HttpServer& l) const { return addr == l.addr; } - - virtual Text ProcessRequest(Text request, const NetAddr& addr); - virtual Text DefaultResponse(); - virtual Text ErrorResponse(); - - virtual bool DoGet(HttpRequest& request, HttpResponse& response); - virtual bool DoPost(HttpRequest& request, HttpResponse& response); - virtual bool DoHead(HttpRequest& request, HttpResponse& response); - - virtual Text GetServerName(); - virtual void SetServerName(const char* name); - -protected: - Text http_server_name; -}; - -// +-------------------------------------------------------------------+ - -class HttpParam -{ -public: - static const char* TYPENAME() { return "HttpParam"; } - - HttpParam(const char* n, const char* v) : name(n), value(v) { } - - int operator == (const HttpParam& p) const { return name == p.name; } - - Text name; - Text value; -}; - -// +-------------------------------------------------------------------+ - -class HttpRequest -{ -public: - static const char* TYPENAME() { return "HttpRequest"; } - - enum METHOD { - HTTP_OPTIONS, - HTTP_GET, - HTTP_HEAD, - HTTP_POST, - HTTP_PUT, - HTTP_DELETE, - HTTP_TRACE, - HTTP_CONNECT - }; - - HttpRequest(const char* request=0); - ~HttpRequest(); - - operator Text(); - - void ParseRequest(Text request); - void ParseCookie(const char* param); - - int Method() const { return method; } - Text URI() const { return uri; } - Text Content() const { return content; } - Text RequestLine() const { return request_line; } - - List& GetQuery() { return query; } - List& GetHeaders() { return headers; } - List& GetCookies() { return cookies; } - - NetAddr GetClientAddr() const { return client_addr; } - void SetClientAddr(const NetAddr& a) { client_addr = a; } - - Text GetParam(const char* name); - - Text GetHeader(const char* name); - void SetHeader(const char* name, const char* value); - void AddHeader(const char* name, const char* value); - Text GetCookie(const char* name); - void SetCookie(const char* name, const char* value); - void AddCookie(const char* name, const char* value); - - Text DecodeParam(const char* value); - static Text EncodeParam(const char* value); - -private: - int method; - Text uri; - Text content; - Text request_line; - NetAddr client_addr; - - List query; - List headers; - List cookies; -}; - -// +-------------------------------------------------------------------+ - -class HttpResponse -{ -public: - static const char* TYPENAME() { return "HttpResponse"; } - - enum STATUS { - SC_CONTINUE = 100, - SC_SWITCHING_PROTOCOLS = 101, - - SC_OK = 200, - SC_CREATED = 201, - SC_ACCEPTED = 202, - SC_NON_AUTHORITATIVE = 203, - SC_NO_CONTENT = 204, - SC_RESET_CONTENT = 205, - SC_PARTIAL_CONTENT = 206, - - SC_MULTIPLE_CHOICES = 300, - SC_MOVED_PERMANENTLY = 301, - SC_FOUND = 302, - SC_SEE_OTHER = 303, - SC_NOT_MODIFIED = 304, - SC_USE_PROXY = 305, - SC_TEMPORARY_REDIRECT = 307, - - SC_BAD_REQUEST = 400, - SC_UNAUTHORIZED = 401, - SC_PAYMENT_REQUIRED = 402, - SC_FORBIDDEN = 403, - SC_NOT_FOUND = 404, - SC_METHOD_NOT_ALLOWED = 405, - SC_NOT_ACCEPTABLE = 406, - SC_PROXY_AUTH_REQ = 407, - SC_REQUEST_TIME_OUT = 408, - SC_CONFLICT = 409, - SC_GONE = 410, - SC_LENGTH_REQUIRED = 411, - - SC_SERVER_ERROR = 500, - SC_NOT_IMPLEMENTED = 501, - SC_BAD_GATEWAY = 502, - SC_SERVICE_UNAVAILABLE = 503, - SC_GATEWAY_TIMEOUT = 504, - SC_VERSION_NOT_SUPPORTED= 505 - }; - - - HttpResponse(int status=500, const char* content=0); - HttpResponse(const char* response); - ~HttpResponse(); - - operator Text(); - - void ParseResponse(Text request); - void ParseCookie(const char* param); - - int Status() const { return status; } - void SetStatus(int s) { status = s; } - - Text Content() const { return content; } - void SetContent(Text t) { content = t; } - void AddContent(Text t) { content += t; } - - List& GetHeaders() { return headers; } - List& GetCookies() { return cookies; } - - Text GetHeader(const char* name); - void SetHeader(const char* name, const char* value); - void AddHeader(const char* name, const char* value); - Text GetCookie(const char* name); - void SetCookie(const char* name, const char* value); - void AddCookie(const char* name, const char* value); - - void SendRedirect(const char* url); - -private: - int status; - Text content; - - List headers; - List cookies; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServer.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#ifndef HttpServer_h +#define HttpServer_h + +#include "NetServer.h" + +// +-------------------------------------------------------------------+ + +class HttpParam; +class HttpRequest; +class HttpResponse; + +// +-------------------------------------------------------------------+ + +class HttpServer : public NetServer +{ +public: + static const char* TYPENAME() { return "HttpServer"; } + + HttpServer(WORD port, int poolsize=1); + virtual ~HttpServer(); + + int operator == (const HttpServer& l) const { return addr == l.addr; } + + virtual Text ProcessRequest(Text request, const NetAddr& addr); + virtual Text DefaultResponse(); + virtual Text ErrorResponse(); + + virtual bool DoGet(HttpRequest& request, HttpResponse& response); + virtual bool DoPost(HttpRequest& request, HttpResponse& response); + virtual bool DoHead(HttpRequest& request, HttpResponse& response); + + virtual Text GetServerName(); + virtual void SetServerName(const char* name); + +protected: + Text http_server_name; +}; + +// +-------------------------------------------------------------------+ + +class HttpParam +{ +public: + static const char* TYPENAME() { return "HttpParam"; } + + HttpParam(const char* n, const char* v) : name(n), value(v) { } + + int operator == (const HttpParam& p) const { return name == p.name; } + + Text name; + Text value; +}; + +// +-------------------------------------------------------------------+ + +class HttpRequest +{ +public: + static const char* TYPENAME() { return "HttpRequest"; } + + enum METHOD { + HTTP_OPTIONS, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, + HTTP_TRACE, + HTTP_CONNECT + }; + + HttpRequest(const char* request=0); + ~HttpRequest(); + + operator Text(); + + void ParseRequest(Text request); + void ParseCookie(const char* param); + + int Method() const { return method; } + Text URI() const { return uri; } + Text Content() const { return content; } + Text RequestLine() const { return request_line; } + + List& GetQuery() { return query; } + List& GetHeaders() { return headers; } + List& GetCookies() { return cookies; } + + NetAddr GetClientAddr() const { return client_addr; } + void SetClientAddr(const NetAddr& a) { client_addr = a; } + + Text GetParam(const char* name); + + Text GetHeader(const char* name); + void SetHeader(const char* name, const char* value); + void AddHeader(const char* name, const char* value); + Text GetCookie(const char* name); + void SetCookie(const char* name, const char* value); + void AddCookie(const char* name, const char* value); + + Text DecodeParam(const char* value); + static Text EncodeParam(const char* value); + +private: + int method; + Text uri; + Text content; + Text request_line; + NetAddr client_addr; + + List query; + List headers; + List cookies; +}; + +// +-------------------------------------------------------------------+ + +class HttpResponse +{ +public: + static const char* TYPENAME() { return "HttpResponse"; } + + enum STATUS { + SC_CONTINUE = 100, + SC_SWITCHING_PROTOCOLS = 101, + + SC_OK = 200, + SC_CREATED = 201, + SC_ACCEPTED = 202, + SC_NON_AUTHORITATIVE = 203, + SC_NO_CONTENT = 204, + SC_RESET_CONTENT = 205, + SC_PARTIAL_CONTENT = 206, + + SC_MULTIPLE_CHOICES = 300, + SC_MOVED_PERMANENTLY = 301, + SC_FOUND = 302, + SC_SEE_OTHER = 303, + SC_NOT_MODIFIED = 304, + SC_USE_PROXY = 305, + SC_TEMPORARY_REDIRECT = 307, + + SC_BAD_REQUEST = 400, + SC_UNAUTHORIZED = 401, + SC_PAYMENT_REQUIRED = 402, + SC_FORBIDDEN = 403, + SC_NOT_FOUND = 404, + SC_METHOD_NOT_ALLOWED = 405, + SC_NOT_ACCEPTABLE = 406, + SC_PROXY_AUTH_REQ = 407, + SC_REQUEST_TIME_OUT = 408, + SC_CONFLICT = 409, + SC_GONE = 410, + SC_LENGTH_REQUIRED = 411, + + SC_SERVER_ERROR = 500, + SC_NOT_IMPLEMENTED = 501, + SC_BAD_GATEWAY = 502, + SC_SERVICE_UNAVAILABLE = 503, + SC_GATEWAY_TIMEOUT = 504, + SC_VERSION_NOT_SUPPORTED= 505 + }; + + + HttpResponse(int status=500, const char* content=0); + HttpResponse(const char* response); + ~HttpResponse(); + + operator Text(); + + void ParseResponse(Text request); + void ParseCookie(const char* param); + + int Status() const { return status; } + void SetStatus(int s) { status = s; } + + Text Content() const { return content; } + void SetContent(Text t) { content = t; } + void AddContent(Text t) { content += t; } + + List& GetHeaders() { return headers; } + List& GetCookies() { return cookies; } + + Text GetHeader(const char* name); + void SetHeader(const char* name, const char* value); + void AddHeader(const char* name, const char* value); + Text GetCookie(const char* name); + void SetCookie(const char* name, const char* value); + void AddCookie(const char* name, const char* value); + + void SendRedirect(const char* url); + +private: + int status; + Text content; + + List headers; + List cookies; +}; + + #endif HttpServer_h \ No newline at end of file diff --git a/NetEx/HttpServlet.cpp b/NetEx/HttpServlet.cpp index 504f2fa..17f5084 100644 --- a/NetEx/HttpServlet.cpp +++ b/NetEx/HttpServlet.cpp @@ -1,223 +1,247 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServlet.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "HttpServlet.h" -#include "NetLayer.h" - -#include -#include -#include - -// +-------------------------------------------------------------------+ - -HttpServlet::HttpServlet() - : session(0) -{ } - -HttpServlet::~HttpServlet() -{ } - -// +--------------------------------------------------------------------+ - -bool -HttpServlet::Service(HttpRequest& request, HttpResponse& response) -{ - bool result = false; - - switch (request.Method()) { - case HttpRequest::HTTP_GET: - result = DoGet(request, response); - break; - - case HttpRequest::HTTP_POST: - result = DoPost(request, response); - break; - - case HttpRequest::HTTP_HEAD: - result = DoHead(request, response); - break; - - default: - break; - } - - return result; -} - -// +--------------------------------------------------------------------+ - -bool -HttpServlet::DoGet(HttpRequest& request, HttpResponse& response) -{ - return false; -} - -bool -HttpServlet::DoPost(HttpRequest& request, HttpResponse& response) -{ - return DoGet(request, response); -} - -bool -HttpServlet::DoHead(HttpRequest& request, HttpResponse& response) -{ - if (DoGet(request, response)) { - int len = response.Content().length(); - - char buffer[256]; - sprintf(buffer, "%d", len); - response.SetHeader("Content-Length", buffer); - response.SetContent(""); - - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ -// +--------------------------------------------------------------------+ - -HttpSession::HttpSession() -{ - id = GenerateUniqueID(); - access_time = NetLayer::GetUTC(); -} - -HttpSession::~HttpSession() -{ - attributes.destroy(); -} - -// +--------------------------------------------------------------------+ - -Text -HttpSession::GetAttribute(const char* name) -{ - ListIter iter = attributes; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) - return p->value; - } - - return Text(); -} - -void -HttpSession::SetAttribute(const char* name, const char* value) -{ - ListIter iter = attributes; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = value; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); - if (param) - attributes.append(param); -} - -void -HttpSession::DelAttribute(const char* name) -{ - ListIter iter = attributes; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - delete iter.removeItem(); - return; - } - } -} - -// +--------------------------------------------------------------------+ - -int -HttpSession::GetIntAttribute(const char* name) -{ - ListIter iter = attributes; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - int result = ::atoi(p->value.data()); - return result; - } - } - - return 0; -} - -void -HttpSession::SetIntAttribute(const char* name, int value) -{ - char buf[32]; - sprintf(buf, "%d", value); - - ListIter iter = attributes; - while (++iter) { - HttpParam* p = iter.value(); - - if (p->name == name) { - p->value = buf; - return; - } - } - - HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, buf); - if (param) - attributes.append(param); -} - -void -HttpSession::DelIntAttribute(const char* name) -{ - DelAttribute(name); -} - -// +--------------------------------------------------------------------+ - -Text -HttpSession::GenerateUniqueID() -{ - char unique[17]; - - for (int i = 0; i < 16; i++) { - char c = rand() % 25 + 'a'; - unique[i] = c; - } - - unique[16] = 0; - return unique; -} - -// +--------------------------------------------------------------------+ - -void -HttpSession::Access() -{ - access_time = NetLayer::GetUTC(); -} - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServlet.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "HttpServlet.h" +#include "NetLayer.h" + +#include +#include +#include + +// +-------------------------------------------------------------------+ + +HttpServlet::HttpServlet() + : session(0) +{ } + +HttpServlet::~HttpServlet() +{ } + +// +--------------------------------------------------------------------+ + +bool +HttpServlet::Service(HttpRequest& request, HttpResponse& response) +{ + bool result = false; + + switch (request.Method()) { + case HttpRequest::HTTP_GET: + result = DoGet(request, response); + break; + + case HttpRequest::HTTP_POST: + result = DoPost(request, response); + break; + + case HttpRequest::HTTP_HEAD: + result = DoHead(request, response); + break; + + default: + break; + } + + return result; +} + +// +--------------------------------------------------------------------+ + +bool +HttpServlet::DoGet(HttpRequest& request, HttpResponse& response) +{ + return false; +} + +bool +HttpServlet::DoPost(HttpRequest& request, HttpResponse& response) +{ + return DoGet(request, response); +} + +bool +HttpServlet::DoHead(HttpRequest& request, HttpResponse& response) +{ + if (DoGet(request, response)) { + int len = response.Content().length(); + + char buffer[256]; + sprintf(buffer, "%d", len); + response.SetHeader("Content-Length", buffer); + response.SetContent(""); + + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ + +HttpSession::HttpSession() +{ + id = GenerateUniqueID(); + access_time = NetLayer::GetUTC(); +} + +HttpSession::~HttpSession() +{ + attributes.destroy(); +} + +// +--------------------------------------------------------------------+ + +Text +HttpSession::GetAttribute(const char* name) +{ + ListIter iter = attributes; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) + return p->value; + } + + return Text(); +} + +void +HttpSession::SetAttribute(const char* name, const char* value) +{ + ListIter iter = attributes; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = value; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value); + if (param) + attributes.append(param); +} + +void +HttpSession::DelAttribute(const char* name) +{ + ListIter iter = attributes; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + delete iter.removeItem(); + return; + } + } +} + +// +--------------------------------------------------------------------+ + +int +HttpSession::GetIntAttribute(const char* name) +{ + ListIter iter = attributes; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + int result = ::atoi(p->value.data()); + return result; + } + } + + return 0; +} + +void +HttpSession::SetIntAttribute(const char* name, int value) +{ + char buf[32]; + sprintf(buf, "%d", value); + + ListIter iter = attributes; + while (++iter) { + HttpParam* p = iter.value(); + + if (p->name == name) { + p->value = buf; + return; + } + } + + HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, buf); + if (param) + attributes.append(param); +} + +void +HttpSession::DelIntAttribute(const char* name) +{ + DelAttribute(name); +} + +// +--------------------------------------------------------------------+ + +Text +HttpSession::GenerateUniqueID() +{ + char unique[17]; + + for (int i = 0; i < 16; i++) { + char c = rand() % 25 + 'a'; + unique[i] = c; + } + + unique[16] = 0; + return unique; +} + +// +--------------------------------------------------------------------+ + +void +HttpSession::Access() +{ + access_time = NetLayer::GetUTC(); +} + + diff --git a/NetEx/HttpServlet.h b/NetEx/HttpServlet.h index 99ac06d..e209671 100644 --- a/NetEx/HttpServlet.h +++ b/NetEx/HttpServlet.h @@ -1,85 +1,109 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServlet.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#ifndef HttpServlet_h -#define HttpServlet_h - -#include "HttpServer.h" - -// +-------------------------------------------------------------------+ - -class HttpServlet; -class HttpSession; - -// +-------------------------------------------------------------------+ - -class HttpServlet -{ -public: - static const char* TYPENAME() { return "HttpServlet"; } - - HttpServlet(); - virtual ~HttpServlet(); - - virtual bool Service(HttpRequest& request, HttpResponse& response); - - virtual bool DoGet(HttpRequest& request, HttpResponse& response); - virtual bool DoPost(HttpRequest& request, HttpResponse& response); - virtual bool DoHead(HttpRequest& request, HttpResponse& response); - - virtual HttpSession* GetSession() { return session; } - virtual void SetSession(HttpSession* s) { session = s; } - -protected: - HttpSession* session; -}; - -// +-------------------------------------------------------------------+ - -class HttpSession -{ -public: - static const char* TYPENAME() { return "HttpSession"; } - - HttpSession(); - virtual ~HttpSession(); - - int operator == (const HttpSession& s) const { return id == s.id; } - - Text GenerateUniqueID(); - - Text GetID() const { return id; } - void SetID(const char* i) { id = i; } - int GetLastAccess() const { return access_time;} - void Access(); - - List& GetAttributes() { return attributes; } - - Text GetAttribute(const char* name); - void SetAttribute(const char* name, const char* value); - void DelAttribute(const char* name); - - int GetIntAttribute(const char* name); - void SetIntAttribute(const char* name, int value); - void DelIntAttribute(const char* name); - -protected: - Text id; - int access_time; - List attributes; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServlet.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#ifndef HttpServlet_h +#define HttpServlet_h + +#include "HttpServer.h" + +// +-------------------------------------------------------------------+ + +class HttpServlet; +class HttpSession; + +// +-------------------------------------------------------------------+ + +class HttpServlet +{ +public: + static const char* TYPENAME() { return "HttpServlet"; } + + HttpServlet(); + virtual ~HttpServlet(); + + virtual bool Service(HttpRequest& request, HttpResponse& response); + + virtual bool DoGet(HttpRequest& request, HttpResponse& response); + virtual bool DoPost(HttpRequest& request, HttpResponse& response); + virtual bool DoHead(HttpRequest& request, HttpResponse& response); + + virtual HttpSession* GetSession() { return session; } + virtual void SetSession(HttpSession* s) { session = s; } + +protected: + HttpSession* session; +}; + +// +-------------------------------------------------------------------+ + +class HttpSession +{ +public: + static const char* TYPENAME() { return "HttpSession"; } + + HttpSession(); + virtual ~HttpSession(); + + int operator == (const HttpSession& s) const { return id == s.id; } + + Text GenerateUniqueID(); + + Text GetID() const { return id; } + void SetID(const char* i) { id = i; } + int GetLastAccess() const { return access_time;} + void Access(); + + List& GetAttributes() { return attributes; } + + Text GetAttribute(const char* name); + void SetAttribute(const char* name, const char* value); + void DelAttribute(const char* name); + + int GetIntAttribute(const char* name); + void SetIntAttribute(const char* name, int value); + void DelIntAttribute(const char* name); + +protected: + Text id; + int access_time; + List attributes; +}; + + #endif HttpServlet_h \ No newline at end of file diff --git a/NetEx/HttpServletExec.cpp b/NetEx/HttpServletExec.cpp index bd71149..0027fe0 100644 --- a/NetEx/HttpServletExec.cpp +++ b/NetEx/HttpServletExec.cpp @@ -1,233 +1,256 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServletExec.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "HttpServletExec.h" -#include "HttpServlet.h" -#include "NetLayer.h" - -#include -#include -#include - -// +-------------------------------------------------------------------+ - -class HttpTestServlet : public HttpServlet -{ -public: - HttpTestServlet() { } - virtual ~HttpTestServlet() { } - - virtual bool DoGet(HttpRequest& request, HttpResponse& response) - { - char buffer[1024]; - Text content; - - content = "HttpTestServlet\n"; - content += "\n

HttpTestServlet

\n"; - - content += "

HttpSessionId:

\n"; - if (session) - content += session->GetID(); - else - content += "No Session Found"; - content += "
\n"; - - content += "

URI Requested:

\n"; - content += request.URI(); - content += "
\n"; - - if (request.GetQuery().size() > 0) { - content += "

Query Parameters:

\n"; - - ListIter q_iter = request.GetQuery(); - while (++q_iter) { - HttpParam* q = q_iter.value(); - sprintf_s(buffer, "%s: %s
\n", q->name.data(), q->value.data()); - content += buffer; - } - } - - content += "

Request Headers:

\n"; - ListIter h_iter = request.GetHeaders(); - while (++h_iter) { - HttpParam* h = h_iter.value(); - sprintf_s(buffer, "%s: %s
\n", h->name.data(), h->value.data()); - content += buffer; - } - - if (request.GetCookies().size() > 0) { - content += "

Cookies:

\n"; - ListIter c_iter = request.GetCookies(); - while (++c_iter) { - HttpParam* c = c_iter.value(); - sprintf_s(buffer, "%s: %s
\n", c->name.data(), c->value.data()); - content += buffer; - } - } - - content += "\n\n"; - - response.SetStatus(HttpResponse::SC_OK); - response.AddHeader("MIME-Version", "1.0"); - response.AddHeader("Content-Type", "text/html"); - response.SetContent(content); - - return true; - } -}; - -// +-------------------------------------------------------------------+ -// +-------------------------------------------------------------------+ -// +-------------------------------------------------------------------+ - -DWORD WINAPI HttpServletExecSessionProc(LPVOID link); - -HttpServletExec::HttpServletExec(WORD port, int poolsize) - : HttpServer(port, poolsize), session_timeout(60), exec_shutdown(false) -{ - http_server_name = "Generic HttpServletExec 1.0"; - - DWORD thread_id = 0; - hsession = CreateThread(0, 4096, HttpServletExecSessionProc, - (LPVOID) this, 0, &thread_id); -} - -HttpServletExec::~HttpServletExec() -{ - if (!exec_shutdown) - exec_shutdown = true; - - WaitForSingleObject(hsession, 1000); - CloseHandle(hsession); - hsession = 0; - - sessions.destroy(); -} - -// +--------------------------------------------------------------------+ - -HttpServlet* -HttpServletExec::GetServlet(HttpRequest& request) -{ - return new(__FILE__,__LINE__) HttpTestServlet; -} - -// +--------------------------------------------------------------------+ - -HttpSession* -HttpServletExec::GetSession(HttpRequest& request) -{ - HttpSession* session = 0; - Text reqID = request.GetCookie("SessionID"); - - if (reqID.length() > 0) { - ListIter iter = sessions; - while (++iter && !session) { - HttpSession* s = iter.value(); - - if (s->GetID() == reqID) { - session = s; - session->Access(); - } - } - } - - if (!session) { - session = new(__FILE__,__LINE__) HttpSession; - if (session) { - sessions.append(session); - - ::Print("HttpServletExec created new session '%s' for request '%s'\n", - (const char*) session->GetID(), - (const char*) request.RequestLine()); - } - else { - ::Print("HttpServletExec out of memory for request '%s'\n", - (const char*) request.RequestLine()); - } - } - - return session; -} - -// +--------------------------------------------------------------------+ - -bool -HttpServletExec::DoGet(HttpRequest& request, HttpResponse& response) -{ - bool result = false; - HttpSession* session = GetSession(request); - HttpServlet* servlet = GetServlet(request); - - if (servlet) { - servlet->SetSession(session); - result = servlet->Service(request, response); - delete servlet; - } - - if (result) { - response.SetHeader("Server", http_server_name); - - if (session) - response.SetCookie("SessionID", session->GetID()); - } - - return result; -} - -// +--------------------------------------------------------------------+ - -DWORD WINAPI HttpServletExecSessionProc(LPVOID link) -{ - HttpServletExec* exec = (HttpServletExec*) link; - - if (exec) - return exec->CheckSessions(); - - return (DWORD) E_POINTER; -} - -DWORD -HttpServletExec::CheckSessions() -{ - while (!exec_shutdown) { - sync.acquire(); - - if (sessions.size()) { - ListIter iter = sessions; - while (++iter) { - HttpSession* s = iter.value(); - - if (NetLayer::GetUTC() - s->GetLastAccess() > session_timeout) { - ::Print("HttpServletExec deleting expired session '%s'\n", (const char*) s->GetID()); - delete iter.removeItem(); - } - } - } - - DoSyncedCheck(); - - sync.release(); - Sleep(100); - } - - return 0; -} - -void -HttpServletExec::DoSyncedCheck() -{ -} \ No newline at end of file +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServletExec.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "HttpServletExec.h" +#include "HttpServlet.h" +#include "NetLayer.h" + +#include +#include +#include + +// +-------------------------------------------------------------------+ + +class HttpTestServlet : public HttpServlet +{ +public: + HttpTestServlet() { } + virtual ~HttpTestServlet() { } + + virtual bool DoGet(HttpRequest& request, HttpResponse& response) + { + char buffer[1024]; + Text content; + + content = "HttpTestServlet\n"; + content += "\n

HttpTestServlet

\n"; + + content += "

HttpSessionId:

\n"; + if (session) + content += session->GetID(); + else + content += "No Session Found"; + content += "
\n"; + + content += "

URI Requested:

\n"; + content += request.URI(); + content += "
\n"; + + if (request.GetQuery().size() > 0) { + content += "

Query Parameters:

\n"; + + ListIter q_iter = request.GetQuery(); + while (++q_iter) { + HttpParam* q = q_iter.value(); + sprintf_s(buffer, "%s: %s
\n", q->name.data(), q->value.data()); + content += buffer; + } + } + + content += "

Request Headers:

\n"; + ListIter h_iter = request.GetHeaders(); + while (++h_iter) { + HttpParam* h = h_iter.value(); + sprintf_s(buffer, "%s: %s
\n", h->name.data(), h->value.data()); + content += buffer; + } + + if (request.GetCookies().size() > 0) { + content += "

Cookies:

\n"; + ListIter c_iter = request.GetCookies(); + while (++c_iter) { + HttpParam* c = c_iter.value(); + sprintf_s(buffer, "%s: %s
\n", c->name.data(), c->value.data()); + content += buffer; + } + } + + content += "\n\n"; + + response.SetStatus(HttpResponse::SC_OK); + response.AddHeader("MIME-Version", "1.0"); + response.AddHeader("Content-Type", "text/html"); + response.SetContent(content); + + return true; + } +}; + +// +-------------------------------------------------------------------+ +// +-------------------------------------------------------------------+ +// +-------------------------------------------------------------------+ + +DWORD WINAPI HttpServletExecSessionProc(LPVOID link); + +HttpServletExec::HttpServletExec(WORD port, int poolsize) + : HttpServer(port, poolsize), session_timeout(60), exec_shutdown(false) +{ + http_server_name = "Generic HttpServletExec 1.0"; + + DWORD thread_id = 0; + hsession = CreateThread(0, 4096, HttpServletExecSessionProc, (LPVOID) this, 0, &thread_id); +} + +HttpServletExec::~HttpServletExec() +{ + if (!exec_shutdown) + exec_shutdown = true; + + WaitForSingleObject(hsession, 1000); + CloseHandle(hsession); + hsession = 0; + + sessions.destroy(); +} + +// +--------------------------------------------------------------------+ + +HttpServlet* +HttpServletExec::GetServlet(HttpRequest& request) +{ + return new(__FILE__,__LINE__) HttpTestServlet; +} + +// +--------------------------------------------------------------------+ + +HttpSession* +HttpServletExec::GetSession(HttpRequest& request) +{ + HttpSession* session = 0; + Text reqID = request.GetCookie("SessionID"); + + if (reqID.length() > 0) { + ListIter iter = sessions; + while (++iter && !session) { + HttpSession* s = iter.value(); + + if (s->GetID() == reqID) { + session = s; + session->Access(); + } + } + } + + if (!session) { + session = new(__FILE__,__LINE__) HttpSession; + if (session) { + sessions.append(session); + + ::Print("HttpServletExec created new session '%s' for request '%s'\n", + (const char*) session->GetID(), + (const char*) request.RequestLine()); + } + else { + ::Print("HttpServletExec out of memory for request '%s'\n", + (const char*) request.RequestLine()); + } + } + + return session; +} + +// +--------------------------------------------------------------------+ + +bool +HttpServletExec::DoGet(HttpRequest& request, HttpResponse& response) +{ + bool result = false; + HttpSession* session = GetSession(request); + HttpServlet* servlet = GetServlet(request); + + if (servlet) { + servlet->SetSession(session); + result = servlet->Service(request, response); + delete servlet; + } + + if (result) { + response.SetHeader("Server", http_server_name); + + if (session) + response.SetCookie("SessionID", session->GetID()); + } + + return result; +} + +// +--------------------------------------------------------------------+ + +DWORD WINAPI HttpServletExecSessionProc(LPVOID link) +{ + HttpServletExec* exec = (HttpServletExec*) link; + + if (exec) + return exec->CheckSessions(); + + return (DWORD) E_POINTER; +} + +DWORD +HttpServletExec::CheckSessions() +{ + while (!exec_shutdown) { + sync.acquire(); + + if (sessions.size()) { + ListIter iter = sessions; + while (++iter) { + HttpSession* s = iter.value(); + + if (NetLayer::GetUTC() - s->GetLastAccess() > session_timeout) { + ::Print("HttpServletExec deleting expired session '%s'\n", (const char*) s->GetID()); + delete iter.removeItem(); + } + } + } + + DoSyncedCheck(); + + sync.release(); + Sleep(100); + } + + return 0; +} + +void +HttpServletExec::DoSyncedCheck() +{ +} diff --git a/NetEx/HttpServletExec.h b/NetEx/HttpServletExec.h index bead257..3ae1be1 100644 --- a/NetEx/HttpServletExec.h +++ b/NetEx/HttpServletExec.h @@ -1,57 +1,81 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: HttpServletExec.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#ifndef HttpServletExec_h -#define HttpServletExec_h - -#include "HttpServer.h" - -// +-------------------------------------------------------------------+ - -class HttpServlet; -class HttpSession; - -// +-------------------------------------------------------------------+ - -class HttpServletExec : public HttpServer -{ -public: - static const char* TYPENAME() { return "HttpServletExec"; } - - HttpServletExec(WORD port, int poolsize=1); - virtual ~HttpServletExec(); - - int operator == (const HttpServletExec& l) const { return addr == l.addr; } - - virtual bool DoGet(HttpRequest& request, HttpResponse& response); - - virtual HttpServlet* GetServlet(HttpRequest& request); - virtual HttpSession* GetSession(HttpRequest& request); - - virtual DWORD CheckSessions(); - - virtual int GetSessionTimeout() const { return session_timeout; } - virtual void SetSessionTimeout(int t) { session_timeout = t; } - -protected: - virtual void DoSyncedCheck(); - - List sessions; - int session_timeout; - HANDLE hsession; - bool exec_shutdown; -}; - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: HttpServletExec.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#ifndef HttpServletExec_h +#define HttpServletExec_h + +#include "HttpServer.h" + +// +-------------------------------------------------------------------+ + +class HttpServlet; +class HttpSession; + +// +-------------------------------------------------------------------+ + +class HttpServletExec : public HttpServer +{ +public: + static const char* TYPENAME() { return "HttpServletExec"; } + + HttpServletExec(WORD port, int poolsize=1); + virtual ~HttpServletExec(); + + int operator == (const HttpServletExec& l) const { return addr == l.addr; } + + virtual bool DoGet(HttpRequest& request, HttpResponse& response); + + virtual HttpServlet* GetServlet(HttpRequest& request); + virtual HttpSession* GetSession(HttpRequest& request); + + virtual DWORD CheckSessions(); + + virtual int GetSessionTimeout() const { return session_timeout; } + virtual void SetSessionTimeout(int t) { session_timeout = t; } + +protected: + virtual void DoSyncedCheck(); + + List sessions; + int session_timeout; + HANDLE hsession; + bool exec_shutdown; +}; + #endif HttpServletExec_h \ No newline at end of file diff --git a/NetEx/NetAddr.cpp b/NetEx/NetAddr.cpp index 53d6c79..ea26f0a 100644 --- a/NetEx/NetAddr.cpp +++ b/NetEx/NetAddr.cpp @@ -1,107 +1,131 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetAddr.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Address -*/ - - -#include "MemDebug.h" -#include "NetAddr.h" -#include "NetHost.h" -#include "NetLayer.h" -#include - -NetAddr::NetAddr(const char* host_name, WORD p) - : addr(0), port(p) -{ - if (host_name && *host_name) { - HOSTENT* h = 0; - - if (isdigit(*host_name)) { - DWORD a = inet_addr(host_name); - h = gethostbyaddr((const char*) &a, 4, AF_INET); - } - else { - h = gethostbyname(host_name); - } - - if (h) { - if (h->h_addr_list) { - addr = **(DWORD**) (h->h_addr_list); - } - } - } - - Init(); -} - -NetAddr::NetAddr(DWORD a, WORD p) - : addr(a), port(p) -{ - Init(); -} - -NetAddr::NetAddr(const NetAddr& n) - : addr(n.addr), port(n.port) -{ - Init(); -} - -// +--------------------------------------------------------------------+ - -void -NetAddr::Init() -{ - ZeroMemory(&sadr, sizeof(sadr)); - - sadr.sin_family = AF_INET; - sadr.sin_port = ::htons(port); - sadr.sin_addr.s_addr = addr; -} - -void -NetAddr::InitFromSockAddr() -{ - addr = sadr.sin_addr.s_addr; - port = ::ntohs(sadr.sin_port); -} - -// +--------------------------------------------------------------------+ - -sockaddr* -NetAddr::GetSockAddr() const -{ - return (sockaddr*) &sadr; -} - -size_t -NetAddr::GetSockAddrLength() const -{ - return sizeof(sadr); -} - -// +--------------------------------------------------------------------+ - -void -NetAddr::SetSockAddr(sockaddr* s, int size) -{ - if (s) { - ZeroMemory(&sadr, sizeof(sadr)); - - if (size > sizeof(sadr)) - CopyMemory(&sadr, s, sizeof(sadr)); - else - CopyMemory(&sadr, s, size); - - InitFromSockAddr(); - } -} - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetAddr.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Address +*/ + + +#include "MemDebug.h" +#include "NetAddr.h" +#include "NetHost.h" +#include "NetLayer.h" +#include + +NetAddr::NetAddr(const char* host_name, WORD p) + : addr(0), port(p) +{ + if (host_name && *host_name) { + HOSTENT* h = 0; + + if (isdigit(*host_name)) { + DWORD a = inet_addr(host_name); + h = gethostbyaddr((const char*) &a, 4, AF_INET); + } + else { + h = gethostbyname(host_name); + } + + if (h) { + if (h->h_addr_list) { + addr = **(DWORD**) (h->h_addr_list); + } + } + } + + Init(); +} + +NetAddr::NetAddr(DWORD a, WORD p) + : addr(a), port(p) +{ + Init(); +} + +NetAddr::NetAddr(const NetAddr& n) + : addr(n.addr), port(n.port) +{ + Init(); +} + +// +--------------------------------------------------------------------+ + +void +NetAddr::Init() +{ + ZeroMemory(&sadr, sizeof(sadr)); + + sadr.sin_family = AF_INET; + sadr.sin_port = ::htons(port); + sadr.sin_addr.s_addr = addr; +} + +void +NetAddr::InitFromSockAddr() +{ + addr = sadr.sin_addr.s_addr; + port = ::ntohs(sadr.sin_port); +} + +// +--------------------------------------------------------------------+ + +sockaddr* +NetAddr::GetSockAddr() const +{ + return (sockaddr*) &sadr; +} + +size_t +NetAddr::GetSockAddrLength() const +{ + return sizeof(sadr); +} + +// +--------------------------------------------------------------------+ + +void +NetAddr::SetSockAddr(sockaddr* s, int size) +{ + if (s) { + ZeroMemory(&sadr, sizeof(sadr)); + + if (size > sizeof(sadr)) + CopyMemory(&sadr, s, sizeof(sadr)); + else + CopyMemory(&sadr, s, size); + + InitFromSockAddr(); + } +} + diff --git a/NetEx/NetAddr.h b/NetEx/NetAddr.h index 562dc64..58e6b9c 100644 --- a/NetEx/NetAddr.h +++ b/NetEx/NetAddr.h @@ -1,58 +1,82 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetAddr.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Address (specifically, Internet Protocol) -*/ - - -#ifndef NetAddr_h -#define NetAddr_h - -#include - -// +-------------------------------------------------------------------+ - -class NetAddr -{ -public: - static const char* TYPENAME() { return "NetAddr"; } - - NetAddr(const char* a, WORD p=0); - NetAddr(DWORD a=0, WORD p=0); - NetAddr(const NetAddr& n); - - int operator == (const NetAddr& a) const { return addr==a.addr && port==a.port; } - - DWORD IPAddr() const { return addr; } - BYTE B4() const { return (BYTE) ((addr & 0xff000000) >> 24); } - BYTE B3() const { return (BYTE) ((addr & 0x00ff0000) >> 16); } - BYTE B2() const { return (BYTE) ((addr & 0x0000ff00) >> 8); } - BYTE B1() const { return (BYTE) ((addr & 0x000000ff) ); } - - WORD Port() const { return port; } - void SetPort(WORD p) { port = p; } - - sockaddr* GetSockAddr() const; - size_t GetSockAddrLength() const; - - void SetSockAddr(sockaddr* s, int size); - void InitFromSockAddr(); - -private: - void Init(); - - DWORD addr; // IP addr in host byte order - WORD port; // IP port in host byte order - sockaddr_in sadr; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetAddr.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Address (specifically, Internet Protocol) +*/ + + +#ifndef NetAddr_h +#define NetAddr_h + +#include + +// +-------------------------------------------------------------------+ + +class NetAddr +{ +public: + static const char* TYPENAME() { return "NetAddr"; } + + NetAddr(const char* a, WORD p=0); + NetAddr(DWORD a=0, WORD p=0); + NetAddr(const NetAddr& n); + + int operator == (const NetAddr& a) const { return addr==a.addr && port==a.port; } + + DWORD IPAddr() const { return addr; } + BYTE B4() const { return (BYTE) ((addr & 0xff000000) >> 24); } + BYTE B3() const { return (BYTE) ((addr & 0x00ff0000) >> 16); } + BYTE B2() const { return (BYTE) ((addr & 0x0000ff00) >> 8); } + BYTE B1() const { return (BYTE) ((addr & 0x000000ff) ); } + + WORD Port() const { return port; } + void SetPort(WORD p) { port = p; } + + sockaddr* GetSockAddr() const; + size_t GetSockAddrLength() const; + + void SetSockAddr(sockaddr* s, int size); + void InitFromSockAddr(); + +private: + void Init(); + + DWORD addr; // IP addr in host byte order + WORD port; // IP port in host byte order + sockaddr_in sadr; +}; + + #endif NetAddr_h \ No newline at end of file diff --git a/NetEx/NetClient.cpp b/NetEx/NetClient.cpp index 4f831fb..f31b881 100644 --- a/NetEx/NetClient.cpp +++ b/NetEx/NetClient.cpp @@ -1,116 +1,140 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetClient.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "NetClient.h" -#include "NetHost.h" -#include "NetLayer.h" -#include - -// +-------------------------------------------------------------------+ - -NetClient::NetClient(const NetAddr& server_addr) - : addr(server_addr), sock(0), delta(0), time(0), err(0) -{ -} - -NetClient::~NetClient() -{ - delete sock; -} - -// +--------------------------------------------------------------------+ - -bool -NetClient::Send(Text msg) -{ - if (msg.length() > 0) { - if (sock) - delete sock; - - sock = new(__FILE__,__LINE__) NetSock(addr, true); - delta = 0; - time = timeGetTime(); - - if (!sock) { - err = ERR_NOBUFS; - return false; - } - - err = sock->send(msg); - if (err < 0) { - err = NetLayer::GetLastError(); - return false; - } - - err = sock->shutdown_output(); - if (err < 0) { - err = NetLayer::GetLastError(); - return false; - } - - return true; - } - - else { - delete sock; - sock = 0; - } - - return false; -} - -Text -NetClient::Recv() -{ - Text response; - - if (sock) { - int ready = sock->select(); - - while (!ready && timeGetTime() - time < 2000) { - Sleep(5); - ready = sock->select(); - } - - if (ready) { - Text msg = sock->recv(); - - while (msg.length() > 0) { - response += msg; - msg = sock->recv(); - } - - delta = timeGetTime() - time; - } - - delete sock; - sock = 0; - } - - return response; -} - -Text -NetClient::SendRecv(Text msg) -{ - Text response; - - if (msg.length() > 0 && Send(msg)) { - response = Recv(); - } - - return response; -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetClient.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "NetClient.h" +#include "NetHost.h" +#include "NetLayer.h" +#include + +// +-------------------------------------------------------------------+ + +NetClient::NetClient(const NetAddr& server_addr) + : addr(server_addr), sock(0), delta(0), time(0), err(0) +{ +} + +NetClient::~NetClient() +{ + delete sock; +} + +// +--------------------------------------------------------------------+ + +bool +NetClient::Send(Text msg) +{ + if (msg.length() > 0) { + if (sock) + delete sock; + + sock = new(__FILE__,__LINE__) NetSock(addr, true); + delta = 0; + time = timeGetTime(); + + if (!sock) { + err = ERR_NOBUFS; + return false; + } + + err = sock->send(msg); + if (err < 0) { + err = NetLayer::GetLastError(); + return false; + } + + err = sock->shutdown_output(); + if (err < 0) { + err = NetLayer::GetLastError(); + return false; + } + + return true; + } + + else { + delete sock; + sock = 0; + } + + return false; +} + +Text +NetClient::Recv() +{ + Text response; + + if (sock) { + int ready = sock->select(); + + while (!ready && timeGetTime() - time < 2000) { + Sleep(5); + ready = sock->select(); + } + + if (ready) { + Text msg = sock->recv(); + + while (msg.length() > 0) { + response += msg; + msg = sock->recv(); + } + + delta = timeGetTime() - time; + } + + delete sock; + sock = 0; + } + + return response; +} + +Text +NetClient::SendRecv(Text msg) +{ + Text response; + + if (msg.length() > 0 && Send(msg)) { + response = Recv(); + } + + return response; +} diff --git a/NetEx/NetClient.h b/NetEx/NetClient.h index bbc668a..932847c 100644 --- a/NetEx/NetClient.h +++ b/NetEx/NetClient.h @@ -1,103 +1,127 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetClient.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Stream-oriented network client class -*/ - - -#ifndef NetClient_h -#define NetClient_h - -#include -#include "NetAddr.h" -#include "NetGram.h" -#include "NetSock.h" -#include "List.h" - -// +-------------------------------------------------------------------+ - -class NetClient -{ -public: - static const char* TYPENAME() { return "NetClient"; } - - NetClient(const NetAddr& server_addr); - virtual ~NetClient(); - - int operator == (const NetClient& c) const { return this == &c; } - - NetAddr GetServerAddr() const { return addr; } - - bool Send(Text msg); - Text Recv(); - Text SendRecv(Text msg); - - int GetLastError() const { return err; } - DWORD GetTime() const { return delta; } - -protected: - NetAddr addr; - NetSock* sock; - DWORD delta; - DWORD time; - int err; - -public: - enum ERRS { - ERR_INTR = 10004, - ERR_BADF = 10009, - ERR_ACCES = 10013, - ERR_FAULT = 10014, - ERR_INVAL = 10022, - ERR_MFILE = 10024, - - ERR_WOULDBLOCK = 10035, - ERR_INPROGRESS = 10036, - ERR_ALREADY = 10037, - ERR_NOTSOCK = 10038, - ERR_DESTADDRREQ = 10039, - ERR_MSGSIZE = 10040, - ERR_PROTOTYPE = 10041, - ERR_NOPROTOOPT = 10042, - ERR_PROTONOSUPPORT = 10043, - ERR_SOCKTNOSUPPORT = 10044, - ERR_OPNOTSUPP = 10045, - ERR_PFNOSUPPORT = 10046, - ERR_AFNOSUPPORT = 10047, - ERR_ADDRINUSE = 10048, - ERR_ADDRNOTAVAIL = 10049, - ERR_NETDOWN = 10050, - ERR_NETUNREACH = 10051, - ERR_NETRESET = 10052, - ERR_CONNABORTED = 10053, - ERR_CONNRESET = 10054, - ERR_NOBUFS = 10055, - ERR_ISCONN = 10056, - ERR_NOTCONN = 10057, - ERR_SHUTDOWN = 10058, - ERR_TOOMANYREFS = 10059, - ERR_TIMEDOUT = 10060, - ERR_CONNREFUSED = 10061, - ERR_LOOP = 10062, - ERR_NAMETOOLONG = 10063, - ERR_HOSTDOWN = 10064, - ERR_HOSTUNREACH = 10065, - ERR_NOTEMPTY = 10066, - ERR_PROCLIM = 10067, - ERR_USERS = 10068, - ERR_DQUOT = 10069, - ERR_STALE = 10070, - ERR_REMOTE = 10071 - }; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetClient.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Stream-oriented network client class +*/ + + +#ifndef NetClient_h +#define NetClient_h + +#include +#include "NetAddr.h" +#include "NetGram.h" +#include "NetSock.h" +#include "List.h" + +// +-------------------------------------------------------------------+ + +class NetClient +{ +public: + static const char* TYPENAME() { return "NetClient"; } + + NetClient(const NetAddr& server_addr); + virtual ~NetClient(); + + int operator == (const NetClient& c) const { return this == &c; } + + NetAddr GetServerAddr() const { return addr; } + + bool Send(Text msg); + Text Recv(); + Text SendRecv(Text msg); + + int GetLastError() const { return err; } + DWORD GetTime() const { return delta; } + +protected: + NetAddr addr; + NetSock* sock; + DWORD delta; + DWORD time; + int err; + +public: + enum ERRS { + ERR_INTR = 10004, + ERR_BADF = 10009, + ERR_ACCES = 10013, + ERR_FAULT = 10014, + ERR_INVAL = 10022, + ERR_MFILE = 10024, + + ERR_WOULDBLOCK = 10035, + ERR_INPROGRESS = 10036, + ERR_ALREADY = 10037, + ERR_NOTSOCK = 10038, + ERR_DESTADDRREQ = 10039, + ERR_MSGSIZE = 10040, + ERR_PROTOTYPE = 10041, + ERR_NOPROTOOPT = 10042, + ERR_PROTONOSUPPORT = 10043, + ERR_SOCKTNOSUPPORT = 10044, + ERR_OPNOTSUPP = 10045, + ERR_PFNOSUPPORT = 10046, + ERR_AFNOSUPPORT = 10047, + ERR_ADDRINUSE = 10048, + ERR_ADDRNOTAVAIL = 10049, + ERR_NETDOWN = 10050, + ERR_NETUNREACH = 10051, + ERR_NETRESET = 10052, + ERR_CONNABORTED = 10053, + ERR_CONNRESET = 10054, + ERR_NOBUFS = 10055, + ERR_ISCONN = 10056, + ERR_NOTCONN = 10057, + ERR_SHUTDOWN = 10058, + ERR_TOOMANYREFS = 10059, + ERR_TIMEDOUT = 10060, + ERR_CONNREFUSED = 10061, + ERR_LOOP = 10062, + ERR_NAMETOOLONG = 10063, + ERR_HOSTDOWN = 10064, + ERR_HOSTUNREACH = 10065, + ERR_NOTEMPTY = 10066, + ERR_PROCLIM = 10067, + ERR_USERS = 10068, + ERR_DQUOT = 10069, + ERR_STALE = 10070, + ERR_REMOTE = 10071 + }; +}; + + #endif NetClient_h \ No newline at end of file diff --git a/NetEx/NetGram.cpp b/NetEx/NetGram.cpp index 94e1ca2..3bb8f2a 100644 --- a/NetEx/NetGram.cpp +++ b/NetEx/NetGram.cpp @@ -1,111 +1,135 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetGram.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Generic Network Packet (Datagram) Implementation -*/ - - -#include "MemDebug.h" -#include "NetGram.h" -#include "NetLayer.h" - -// +-------------------------------------------------------------------+ - -static DWORD net_gram_sequence = 1; - -// +-------------------------------------------------------------------+ - -/** - * NetGram constructor for ACK packets - */ -NetGram::NetGram() - : retries(0), packet_id(0), send_time(0) -{ } - -/** - * NetGram constructor for receiving packets from remote hosts - */ -NetGram::NetGram(const NetAddr& src, Text msg) - : addr(src), retries(0), send_time(0) -{ - body = msg; - - if (body.length() >= NET_GRAM_HEADER_SIZE) { - BYTE* data = (BYTE*) body.data(); - - packet_id = (((DWORD) data[0]) << 24) + - (((DWORD) data[1]) << 16) + - (((DWORD) data[2]) << 8) + - ((DWORD) data[3]); - } -} - -/** - * NetGram constructor for composing packets to send to remote hosts - */ -NetGram::NetGram(const NetAddr& dst, Text user_data, int r) - : addr(dst), retries(r) -{ - send_time = NetLayer::GetTime(); - packet_id = net_gram_sequence++; - - if (retries) - packet_id |= NET_GRAM_RELIABLE; - - static BYTE buf[NET_GRAM_MAX_SIZE]; - buf[0] = (BYTE) (packet_id >> 24) & 0xff; - buf[1] = (BYTE) (packet_id >> 16) & 0xff; - buf[2] = (BYTE) (packet_id >> 8) & 0xff; - buf[3] = (BYTE) (packet_id) & 0xff; - - int len = user_data.length(); - if (len >= NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE) - len = NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE - 1; - - CopyMemory(buf+NET_GRAM_HEADER_SIZE, user_data.data(), len); - - body = Text((char*) buf, len+NET_GRAM_HEADER_SIZE); -} - -// +--------------------------------------------------------------------+ - -void -NetGram::Retry() -{ - if (retries > 0) { - retries--; - send_time = NetLayer::GetTime(); - } -} - -// +--------------------------------------------------------------------+ - -NetGram -NetGram::Ack() -{ - NetGram ack; - - ack.packet_id = packet_id | NET_GRAM_ACK; - ack.send_time = NetLayer::GetTime(); - - static BYTE buf[NET_GRAM_HEADER_SIZE]; - buf[0] = (BYTE) (ack.packet_id >> 24) & 0xff; - buf[1] = (BYTE) (ack.packet_id >> 16) & 0xff; - buf[2] = (BYTE) (ack.packet_id >> 8) & 0xff; - buf[3] = (BYTE) (ack.packet_id) & 0xff; - - ack.body = Text((char*) buf, NET_GRAM_HEADER_SIZE); - - return ack; -} - - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetGram.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Generic Network Packet (Datagram) Implementation +*/ + + +#include "MemDebug.h" +#include "NetGram.h" +#include "NetLayer.h" + +// +-------------------------------------------------------------------+ + +static DWORD net_gram_sequence = 1; + +// +-------------------------------------------------------------------+ + +/** + * NetGram constructor for ACK packets + */ +NetGram::NetGram() + : retries(0), packet_id(0), send_time(0) +{ } + +/** + * NetGram constructor for receiving packets from remote hosts + */ +NetGram::NetGram(const NetAddr& src, Text msg) + : addr(src), retries(0), send_time(0) +{ + body = msg; + + if (body.length() >= NET_GRAM_HEADER_SIZE) { + BYTE* data = (BYTE*) body.data(); + + packet_id = (((DWORD) data[0]) << 24) + + (((DWORD) data[1]) << 16) + + (((DWORD) data[2]) << 8) + + ((DWORD) data[3]); + } +} + +/** + * NetGram constructor for composing packets to send to remote hosts + */ +NetGram::NetGram(const NetAddr& dst, Text user_data, int r) + : addr(dst), retries(r) +{ + send_time = NetLayer::GetTime(); + packet_id = net_gram_sequence++; + + if (retries) + packet_id |= NET_GRAM_RELIABLE; + + static BYTE buf[NET_GRAM_MAX_SIZE]; + buf[0] = (BYTE) (packet_id >> 24) & 0xff; + buf[1] = (BYTE) (packet_id >> 16) & 0xff; + buf[2] = (BYTE) (packet_id >> 8) & 0xff; + buf[3] = (BYTE) (packet_id) & 0xff; + + int len = user_data.length(); + if (len >= NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE) + len = NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE - 1; + + CopyMemory(buf+NET_GRAM_HEADER_SIZE, user_data.data(), len); + + body = Text((char*) buf, len+NET_GRAM_HEADER_SIZE); +} + +// +--------------------------------------------------------------------+ + +void +NetGram::Retry() +{ + if (retries > 0) { + retries--; + send_time = NetLayer::GetTime(); + } +} + +// +--------------------------------------------------------------------+ + +NetGram +NetGram::Ack() +{ + NetGram ack; + + ack.packet_id = packet_id | NET_GRAM_ACK; + ack.send_time = NetLayer::GetTime(); + + static BYTE buf[NET_GRAM_HEADER_SIZE]; + buf[0] = (BYTE) (ack.packet_id >> 24) & 0xff; + buf[1] = (BYTE) (ack.packet_id >> 16) & 0xff; + buf[2] = (BYTE) (ack.packet_id >> 8) & 0xff; + buf[3] = (BYTE) (ack.packet_id) & 0xff; + + ack.body = Text((char*) buf, NET_GRAM_HEADER_SIZE); + + return ack; +} + + + diff --git a/NetEx/NetGram.h b/NetEx/NetGram.h index e836e8d..d1831b4 100644 --- a/NetEx/NetGram.h +++ b/NetEx/NetGram.h @@ -1,79 +1,103 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetGram.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Datagram (UDP) packet that implements the basic - packet-oriented network protocol. -*/ - - -#ifndef NetGram_h -#define NetGram_h - -#include -#include "NetAddr.h" -#include "Text.h" - -// +-------------------------------------------------------------------+ - -const int NET_GRAM_ACK = 0x80000000; -const int NET_GRAM_RELIABLE = 0x40000000; -const int NET_GRAM_SEQ_MASK = 0x3fffffff; - -const int NET_GRAM_HEADER_SIZE = 4; -const int NET_GRAM_MAX_SIZE = 1024; - -// +-------------------------------------------------------------------+ - -class NetGram -{ -public: - static const char* TYPENAME() { return "NetGram"; } - - // for receiving packets from remote hosts: - NetGram(const NetAddr& src, Text msg); - - // for composing packets to send to remote hosts: - NetGram(const NetAddr& dst, Text user_data, int retries); - - int operator == (const NetGram& g) const { return packet_id == g.packet_id && - addr == g.addr; } - int operator < (const NetGram& g) const { return Sequence() < g.Sequence(); } - - DWORD PacketID() const { return packet_id; } - DWORD Sequence() const { return packet_id & NET_GRAM_SEQ_MASK; } - DWORD SendTime() const { return send_time; } - BYTE* Data() const { return (BYTE*) body.data(); } - BYTE* UserData() const { return (BYTE*) body.data() + NET_GRAM_HEADER_SIZE; } - int Size() const { return body.length(); } - const Text& Body() const { return body; } - const NetAddr& Address() const { return addr; } - - bool IsAck() const { return packet_id & NET_GRAM_ACK ? true : false; } - bool IsReliable() const { return packet_id & NET_GRAM_RELIABLE ? true : false; } - int Retries() const { return retries; } - - void Retry(); - NetGram Ack(); - void ClearAck() { packet_id &= ~NET_GRAM_ACK; } - -protected: - NetGram(); - - NetAddr addr; // network address of remote host - int retries; // number of retries remaining (reliable packets only) - DWORD send_time; // time in msec of most recent send attempt - - DWORD packet_id; // copy of packet id from header in body - Text body; // header plus user data -}; - - -#endif NetGram_h \ No newline at end of file +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetGram.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Datagram (UDP) packet that implements the basic + packet-oriented network protocol. +*/ + + +#ifndef NetGram_h +#define NetGram_h + +#include +#include "NetAddr.h" +#include "Text.h" + +// +-------------------------------------------------------------------+ + +const int NET_GRAM_ACK = 0x80000000; +const int NET_GRAM_RELIABLE = 0x40000000; +const int NET_GRAM_SEQ_MASK = 0x3fffffff; + +const int NET_GRAM_HEADER_SIZE = 4; +const int NET_GRAM_MAX_SIZE = 1024; + +// +-------------------------------------------------------------------+ + +class NetGram +{ +public: + static const char* TYPENAME() { return "NetGram"; } + + // for receiving packets from remote hosts: + NetGram(const NetAddr& src, Text msg); + + // for composing packets to send to remote hosts: + NetGram(const NetAddr& dst, Text user_data, int retries); + + int operator == (const NetGram& g) const { return packet_id == g.packet_id && + addr == g.addr; } + int operator < (const NetGram& g) const { return Sequence() < g.Sequence(); } + + DWORD PacketID() const { return packet_id; } + DWORD Sequence() const { return packet_id & NET_GRAM_SEQ_MASK; } + DWORD SendTime() const { return send_time; } + BYTE* Data() const { return (BYTE*) body.data(); } + BYTE* UserData() const { return (BYTE*) body.data() + NET_GRAM_HEADER_SIZE; } + int Size() const { return body.length(); } + const Text& Body() const { return body; } + const NetAddr& Address() const { return addr; } + + bool IsAck() const { return packet_id & NET_GRAM_ACK ? true : false; } + bool IsReliable() const { return packet_id & NET_GRAM_RELIABLE ? true : false; } + int Retries() const { return retries; } + + void Retry(); + NetGram Ack(); + void ClearAck() { packet_id &= ~NET_GRAM_ACK; } + +protected: + NetGram(); + + NetAddr addr; // network address of remote host + int retries; // number of retries remaining (reliable packets only) + DWORD send_time; // time in msec of most recent send attempt + + DWORD packet_id; // copy of packet id from header in body + Text body; // header plus user data +}; + + +#endif NetGram_h diff --git a/NetEx/NetHost.cpp b/NetEx/NetHost.cpp index d42ec2a..03330f0 100644 --- a/NetEx/NetHost.cpp +++ b/NetEx/NetHost.cpp @@ -1,103 +1,127 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetHost.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Host -*/ - - -#include "MemDebug.h" -#include "NetHost.h" -#include "NetLayer.h" -#include - -NetHost::NetHost() -{ - char host_name[256]; - gethostname(host_name, sizeof(host_name)); - - Init(host_name); -} - -NetHost::NetHost(const char* host_name) -{ - Init(host_name); -} - -void NetHost::Init(const char* host_name) -{ - if (host_name && *host_name) { - HOSTENT* h = 0; - - if (isdigit(*host_name)) { - DWORD addr = inet_addr(host_name); - h = gethostbyaddr((const char*) &addr, 4, AF_INET); - } - else { - h = gethostbyname(host_name); - } - - if (h) { - name = h->h_name; - - char** alias = h->h_aliases; - while (*alias) { - aliases.append(new Text(*alias)); - alias++; - } - - char** addr = h->h_addr_list; - while (*addr) { - NetAddr* pna = new(__FILE__,__LINE__) NetAddr(**(DWORD**) addr); - if (pna) - addresses.append(pna); - addr++; - } - } - } -} - -NetHost::NetHost(const NetHost& n) -{ - if (&n != this) { - NetHost& nh = (NetHost&) n; - - name = nh.name; - - ListIter alias = nh.aliases; - while (++alias) - aliases.append(new Text(*alias.value())); - - ListIter addr = nh.addresses; - while (++addr) - addresses.append(new NetAddr(*addr.value())); - } -} - -NetHost::~NetHost() -{ - aliases.destroy(); - addresses.destroy(); -} - -const char* -NetHost::Name() -{ - return name; -} - -NetAddr -NetHost::Address() -{ - if (addresses.size()) - return *(addresses[0]); - - return NetAddr((DWORD) 0); +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetHost.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Host +*/ + + +#include "MemDebug.h" +#include "NetHost.h" +#include "NetLayer.h" +#include + +NetHost::NetHost() +{ + char host_name[256]; + gethostname(host_name, sizeof(host_name)); + + Init(host_name); +} + +NetHost::NetHost(const char* host_name) +{ + Init(host_name); +} + +void NetHost::Init(const char* host_name) +{ + if (host_name && *host_name) { + HOSTENT* h = 0; + + if (isdigit(*host_name)) { + DWORD addr = inet_addr(host_name); + h = gethostbyaddr((const char*) &addr, 4, AF_INET); + } + else { + h = gethostbyname(host_name); + } + + if (h) { + name = h->h_name; + + char** alias = h->h_aliases; + while (*alias) { + aliases.append(new Text(*alias)); + alias++; + } + + char** addr = h->h_addr_list; + while (*addr) { + NetAddr* pna = new(__FILE__,__LINE__) NetAddr(**(DWORD**) addr); + if (pna) + addresses.append(pna); + addr++; + } + } + } +} + +NetHost::NetHost(const NetHost& n) +{ + if (&n != this) { + NetHost& nh = (NetHost&) n; + + name = nh.name; + + ListIter alias = nh.aliases; + while (++alias) + aliases.append(new Text(*alias.value())); + + ListIter addr = nh.addresses; + while (++addr) + addresses.append(new NetAddr(*addr.value())); + } +} + +NetHost::~NetHost() +{ + aliases.destroy(); + addresses.destroy(); +} + +const char* +NetHost::Name() +{ + return name; +} + +NetAddr +NetHost::Address() +{ + if (addresses.size()) + return *(addresses[0]); + + return NetAddr((DWORD) 0); } \ No newline at end of file diff --git a/NetEx/NetHost.h b/NetEx/NetHost.h index 2240eab..cf6c255 100644 --- a/NetEx/NetHost.h +++ b/NetEx/NetHost.h @@ -1,50 +1,74 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetHost.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Host -*/ - -#ifndef NET_HOST_H -#define NET_HOST_H - -#include -#include "NetAddr.h" -#include "Text.h" -#include "List.h" - -// +-------------------------------------------------------------------+ - -class NetHost -{ -public: - static const char* TYPENAME() { return "NetHost"; } - - NetHost(); - NetHost(const char* host_addr); - NetHost(const NetHost& n); - ~NetHost(); - - const char* Name(); - NetAddr Address(); - - List& Aliases() { return aliases; } - List& AddressList() { return addresses; } - -private: - void Init(const char* host_name); - - Text name; - List aliases; - List addresses; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetHost.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Host +*/ + +#ifndef NET_HOST_H +#define NET_HOST_H + +#include +#include "NetAddr.h" +#include "Text.h" +#include "List.h" + +// +-------------------------------------------------------------------+ + +class NetHost +{ +public: + static const char* TYPENAME() { return "NetHost"; } + + NetHost(); + NetHost(const char* host_addr); + NetHost(const NetHost& n); + ~NetHost(); + + const char* Name(); + NetAddr Address(); + + List& Aliases() { return aliases; } + List& AddressList() { return addresses; } + +private: + void Init(const char* host_name); + + Text name; + List aliases; + List addresses; +}; + + #endif // NET_HOST_H \ No newline at end of file diff --git a/NetEx/NetLayer.cpp b/NetEx/NetLayer.cpp index 8494f72..573fe45 100644 --- a/NetEx/NetLayer.cpp +++ b/NetEx/NetLayer.cpp @@ -1,93 +1,117 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetLayer.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Wrapper for WinSock Library -*/ - - -#include "MemDebug.h" -#include -#include "NetLayer.h" - -#include -#include - -// +-------------------------------------------------------------------+ - -static DWORD baseTime = timeGetTime(); - -// +-------------------------------------------------------------------+ - -NetLayer::NetLayer() -{ - fail = false; - ZeroMemory(&info, sizeof(info)); - - WORD ver = MAKEWORD(2,2); - int err = WSAStartup(ver, &info); - - if (err) - fail = true; -} - -NetLayer::~NetLayer() -{ - WSACleanup(); -} - -bool -NetLayer::OK() const -{ - return !fail; -} - -const char* -NetLayer::Description() const -{ - return info.szDescription; -} - -int -NetLayer::GetLastError() -{ - return WSAGetLastError(); -} - -DWORD -NetLayer::GetTime() -{ - DWORD msec = timeGetTime(); - - if (msec >= baseTime) { - return msec - baseTime; - } - - else { - DWORD extra = 0xffffffff; - return msec + extra - baseTime; - } -} - -long -NetLayer::GetUTC() -{ - return (long) time(0); -} - -Text -NetLayer::GetHostName() -{ - char hostname[256]; - ZeroMemory(hostname, sizeof(hostname)); - ::gethostname(hostname, sizeof(hostname)); - - return hostname; -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetLayer.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Wrapper for WinSock Library +*/ + + +#include "MemDebug.h" +#include +#include "NetLayer.h" + +#include +#include + +// +-------------------------------------------------------------------+ + +static DWORD baseTime = timeGetTime(); + +// +-------------------------------------------------------------------+ + +NetLayer::NetLayer() +{ + fail = false; + ZeroMemory(&info, sizeof(info)); + + WORD ver = MAKEWORD(2,2); + int err = WSAStartup(ver, &info); + + if (err) + fail = true; +} + +NetLayer::~NetLayer() +{ + WSACleanup(); +} + +bool +NetLayer::OK() const +{ + return !fail; +} + +const char* +NetLayer::Description() const +{ + return info.szDescription; +} + +int +NetLayer::GetLastError() +{ + return WSAGetLastError(); +} + +DWORD +NetLayer::GetTime() +{ + DWORD msec = timeGetTime(); + + if (msec >= baseTime) { + return msec - baseTime; + } + + else { + DWORD extra = 0xffffffff; + return msec + extra - baseTime; + } +} + +long +NetLayer::GetUTC() +{ + return (long) time(0); +} + +Text +NetLayer::GetHostName() +{ + char hostname[256]; + ZeroMemory(hostname, sizeof(hostname)); + ::gethostname(hostname, sizeof(hostname)); + + return hostname; +} diff --git a/NetEx/NetLayer.h b/NetEx/NetLayer.h index fbb2b52..66b9c9d 100644 --- a/NetEx/NetLayer.h +++ b/NetEx/NetLayer.h @@ -1,45 +1,69 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetLayer.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Wrapper for WinSock Library -*/ - -#ifndef NetLayer_h -#define NetLayer_h - -#include -#include "Text.h" - -// +-------------------------------------------------------------------+ - -class NetLayer -{ -public: - static const char* TYPENAME() { return "NetLayer"; } - - NetLayer(); - ~NetLayer(); - - bool OK() const; - const char* Description() const; - - static int GetLastError(); - static DWORD GetTime(); - static long GetUTC(); - static Text GetHostName(); - -private: - WSADATA info; - bool fail; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetLayer.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Wrapper for WinSock Library +*/ + +#ifndef NetLayer_h +#define NetLayer_h + +#include +#include "Text.h" + +// +-------------------------------------------------------------------+ + +class NetLayer +{ +public: + static const char* TYPENAME() { return "NetLayer"; } + + NetLayer(); + ~NetLayer(); + + bool OK() const; + const char* Description() const; + + static int GetLastError(); + static DWORD GetTime(); + static long GetUTC(); + static Text GetHostName(); + +private: + WSADATA info; + bool fail; +}; + + #endif NetLayer_h \ No newline at end of file diff --git a/NetEx/NetLink.cpp b/NetEx/NetLink.cpp index 7cd6f4e..05c486a 100644 --- a/NetEx/NetLink.cpp +++ b/NetEx/NetLink.cpp @@ -1,487 +1,511 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetLink.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network (IP) Socket Wrapper Implementation -*/ - - -#include "MemDebug.h" -#include "NetLink.h" -#include "NetGram.h" -#include "NetMsg.h" -#include "NetPeer.h" -#include "NetLayer.h" - -// +-------------------------------------------------------------------+ - -DWORD WINAPI NetLinkProc(LPVOID link); - -const DWORD UDP_HEADER_SIZE = 34; - -// +-------------------------------------------------------------------+ -// client-side ctor -NetLink::NetLink() - : hnet(0), shutdown(false), traffic_time(50), resend_time(300), - packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100) -{ - ZeroMemory(lag_samples, sizeof(lag_samples)); - lag_index = 0; - - DWORD thread_id = 0; - hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id); -} - -// server-side ctor -NetLink::NetLink(NetAddr& a) - : addr(a), hnet(0), shutdown(false), traffic_time(50), resend_time(300), - packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100) -{ - ZeroMemory(lag_samples, sizeof(lag_samples)); - lag_index = 0; - - sock.bind(addr); - DWORD thread_id = 0; - hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id); -} - -NetLink::~NetLink() -{ - if (!shutdown) { - shutdown = true; - } - - if (hnet) { - WaitForSingleObject(hnet, 2000); - CloseHandle(hnet); - } - - send_list.destroy(); // packets waiting to be ack'ed must be destroyed - recv_list.clear(); // received messages are owned by the peers - peer_list.destroy(); // but the net link owns the peers! -} - -// +--------------------------------------------------------------------+ - -static DWORD base_netid = 1000; - -DWORD -NetLink::AddPeer(const char* a, WORD p) -{ - return AddPeer(NetAddr(a, p)); -} - -DWORD -NetLink::AddPeer(DWORD a, WORD p) -{ - return AddPeer(NetAddr(a, p)); -} - -DWORD -NetLink::AddPeer(const NetAddr& a) -{ - if (!a.IPAddr()) - return 0; - - AutoThreadSync auto_sync(sync); - - NetPeer* peer = FindPeer(a); - - if (!peer) { - peer = new(__FILE__, __LINE__) NetPeer(a, base_netid++); - if (peer) - peer_list.append(peer); - } - - if (peer) - return peer->NetID(); - - return 0; -} - -// +--------------------------------------------------------------------+ - -bool -NetLink::SendMessage(DWORD nid, void* d, int l, BYTE f) -{ - return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, d, l, f)); -} - -bool -NetLink::SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f) -{ - return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, type, text, len, f)); -} - -bool -NetLink::SendMessage(NetMsg* msg) -{ - if (msg) { - if (msg->Type() != NetMsg::INVALID && - msg->Type() < NetMsg::RESERVED && - msg->NetID()) { - - NetPeer* p = FindPeer(msg->NetID()); - if (p) - return p->SendMessage(msg); - } - - delete msg; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -NetMsg* -NetLink::GetMessage(DWORD netid) -{ - NetMsg* msg = 0; - - // receive from specific host: - if (netid) { - NetPeer* p = FindPeer(netid); - if (p) { - msg = p->GetMessage(); - - sync.acquire(); - recv_list.remove(msg); - sync.release(); - } - } - - return msg; -} - -// +--------------------------------------------------------------------+ - -NetMsg* -NetLink::GetMessage() -{ - NetMsg* msg = 0; - - // get first available packet: - - // Double-checked locking: - if (recv_list.size()) { - sync.acquire(); - if (recv_list.size()) { - msg = recv_list.removeIndex(0); - } - sync.release(); - - if (msg && msg->NetID()) { - NetPeer* p = FindPeer(msg->NetID()); - if (p) { - p->GetMessage(); // remove message from peer's list - // don't do this inside of sync block - - // it might cause a deadlock - } - } - } - - return msg; -} - -// +--------------------------------------------------------------------+ - -void -NetLink::Shutdown() -{ - shutdown = true; -} - -// +--------------------------------------------------------------------+ - -DWORD WINAPI NetLinkProc(LPVOID link) -{ - NetLink* netlink = (NetLink*) link; - - if (netlink) - return netlink->DoSendRecv(); - - return (DWORD) E_POINTER; -} - -DWORD -NetLink::DoSendRecv() -{ - while (!shutdown) { - ReadPackets(); - SendPackets(); - - // discard reeeeally old peers: - sync.acquire(); - - ListIter iter = peer_list; - while (!shutdown && ++iter) { - NetPeer* peer = iter.value(); - - if ((NetLayer::GetUTC() - peer->LastReceiveTime()) > 300) - delete iter.removeItem(); - } - - sync.release(); - Sleep(traffic_time); - } - - return 0; -} - -void -NetLink::ReadPackets() -{ - while (!shutdown && sock.select(NetSock::SELECT_READ) > 0) { - NetGram* gram = RecvNetGram(); - - if (gram && gram->IsReliable()) { - if (gram->IsAck()) { - ProcessAck(gram); - delete gram; - } - else { - AckNetGram(gram); - QueueNetGram(gram); - } - } - else { - QueueNetGram(gram); - } - } -} - -void -NetLink::SendPackets() -{ - if (shutdown) - return; - - if (sock.select(NetSock::SELECT_WRITE) > 0) { - DoRetries(); - } - - AutoThreadSync auto_sync(sync); - - ListIter iter = peer_list; - while (!shutdown && ++iter) { - NetPeer* p = iter.value(); - NetGram* g = 0; - - do { - if (sock.select(NetSock::SELECT_WRITE) > 0) { - g = p->ComposeGram(); - if (g) { - SendNetGram(g); - } - } - else { - g = 0; - } - } - while (!shutdown && g); - } -} - -// +--------------------------------------------------------------------+ - -void -NetLink::SendNetGram(NetGram* gram) -{ - if (gram) { - if (gram->IsReliable()) { - send_list.append(gram); - } - - int err = sock.sendto(gram->Body(), gram->Address()); - - if (err < 0) { - err = NetLayer::GetLastError(); - } - else { - packets_sent += 1; - bytes_sent += gram->Size() + UDP_HEADER_SIZE; - } - - if (!gram->IsReliable()) - delete gram; - } -} - -NetGram* -NetLink::RecvNetGram() -{ - NetAddr from; - Text msg = sock.recvfrom(&from); - - packets_recv += 1; - bytes_recv += msg.length() + UDP_HEADER_SIZE; - - return new(__FILE__, __LINE__) NetGram(from, msg); -} - -// +--------------------------------------------------------------------+ - -void -NetLink::AckNetGram(NetGram* gram) -{ - if (gram) { - NetGram ack = gram->Ack(); - - int err = sock.sendto(ack.Body(), gram->Address()); - if (err < 0) - err = NetLayer::GetLastError(); - } - else { - Print("NetLink::AckNetGram( NULL!!! )\n"); - } -} - -void -NetLink::ProcessAck(NetGram* gram) -{ - if (!shutdown && send_list.size()) { - AutoThreadSync auto_sync(sync); - - // remove the ack flag: - gram->ClearAck(); - - // find a matching outgoing packet: - int sent = send_list.index(gram); - if (sent >= 0) { - NetGram* orig = send_list.removeIndex(sent); - DWORD time = NetLayer::GetTime(); - DWORD msec = time - orig->SendTime(); - double dlag = 0.75 * lag + 0.25 * msec; - - if (lag_index >= 10) lag_index = 0; - lag_samples[lag_index++] = msec; - - NetPeer* peer = FindPeer(orig->Address()); - if (peer) - peer->SetLastReceiveTime(NetLayer::GetUTC()); - - delete orig; - - lag = (DWORD) dlag; - - if (lag > 100) - resend_time = 3 * lag; - else - resend_time = 300; - } - } -} - -void -NetLink::QueueNetGram(NetGram* gram) -{ - if (!shutdown) { - AutoThreadSync auto_sync(sync); - - DWORD sequence = 0; - NetPeer* peer = FindPeer(gram->Address()); - - if (peer) { - sequence = peer->Sequence(); - } - else { - peer = new(__FILE__, __LINE__) NetPeer(gram->Address(), base_netid++); - if (peer) - peer_list.append(peer); - } - - if (!gram->IsReliable()) { - if (gram->Sequence() < sequence) { // discard, too old - delete gram; - return; - } - } - - // sort this gram into the recv list(s) based on sequence: - if (peer) { - peer->ReceiveGram(gram, &recv_list); - } - } -} - -// +--------------------------------------------------------------------+ - -void -NetLink::DoRetries() -{ - if (!shutdown) { - AutoThreadSync auto_sync(sync); - - if (send_list.size()) { - int time = (int) NetLayer::GetTime(); - - ListIter iter = send_list; - while (!shutdown && ++iter) { - NetGram* gram = iter.value(); - - // still trying ? - if (gram->Retries() > 0) { - DWORD last_send = gram->SendTime(); - DWORD delta = time - last_send; - - if (delta > resend_time) { - gram->Retry(); - sock.sendto(gram->Body(), gram->Address()); - retries++; - } - } - - // oh, give it up: - else { - iter.removeItem(); - delete gram; - drops++; - } - } - } - } -} - -// +--------------------------------------------------------------------+ - -NetPeer* -NetLink::FindPeer(DWORD netid) -{ - AutoThreadSync auto_sync(sync); - NetPeer* peer = 0; - - ListIter iter = peer_list; - while (++iter && !peer) { - NetPeer* p = iter.value(); - - if (p->NetID() == netid) - peer = p; - } - - return peer; -} - -NetPeer* -NetLink::FindPeer(const NetAddr& a) -{ - AutoThreadSync auto_sync(sync); - NetPeer* peer = 0; - - ListIter iter = peer_list; - while (++iter && !peer) { - NetPeer* p = iter.value(); - - if (p->Address() == a) - peer = p; - } - - return peer; -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetLink.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network (IP) Socket Wrapper Implementation +*/ + + +#include "MemDebug.h" +#include "NetLink.h" +#include "NetGram.h" +#include "NetMsg.h" +#include "NetPeer.h" +#include "NetLayer.h" + +// +-------------------------------------------------------------------+ + +DWORD WINAPI NetLinkProc(LPVOID link); + +const DWORD UDP_HEADER_SIZE = 34; + +// +-------------------------------------------------------------------+ +// client-side ctor +NetLink::NetLink() + : hnet(0), shutdown(false), traffic_time(50), resend_time(300), + packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100) +{ + ZeroMemory(lag_samples, sizeof(lag_samples)); + lag_index = 0; + + DWORD thread_id = 0; + hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id); +} + +// server-side ctor +NetLink::NetLink(NetAddr& a) + : addr(a), hnet(0), shutdown(false), traffic_time(50), resend_time(300), + packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100) +{ + ZeroMemory(lag_samples, sizeof(lag_samples)); + lag_index = 0; + + sock.bind(addr); + DWORD thread_id = 0; + hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id); +} + +NetLink::~NetLink() +{ + if (!shutdown) { + shutdown = true; + } + + if (hnet) { + WaitForSingleObject(hnet, 2000); + CloseHandle(hnet); + } + + send_list.destroy(); // packets waiting to be ack'ed must be destroyed + recv_list.clear(); // received messages are owned by the peers + peer_list.destroy(); // but the net link owns the peers! +} + +// +--------------------------------------------------------------------+ + +static DWORD base_netid = 1000; + +DWORD +NetLink::AddPeer(const char* a, WORD p) +{ + return AddPeer(NetAddr(a, p)); +} + +DWORD +NetLink::AddPeer(DWORD a, WORD p) +{ + return AddPeer(NetAddr(a, p)); +} + +DWORD +NetLink::AddPeer(const NetAddr& a) +{ + if (!a.IPAddr()) + return 0; + + AutoThreadSync auto_sync(sync); + + NetPeer* peer = FindPeer(a); + + if (!peer) { + peer = new(__FILE__, __LINE__) NetPeer(a, base_netid++); + if (peer) + peer_list.append(peer); + } + + if (peer) + return peer->NetID(); + + return 0; +} + +// +--------------------------------------------------------------------+ + +bool +NetLink::SendMessage(DWORD nid, void* d, int l, BYTE f) +{ + return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, d, l, f)); +} + +bool +NetLink::SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f) +{ + return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, type, text, len, f)); +} + +bool +NetLink::SendMessage(NetMsg* msg) +{ + if (msg) { + if (msg->Type() != NetMsg::INVALID && + msg->Type() < NetMsg::RESERVED && + msg->NetID()) { + + NetPeer* p = FindPeer(msg->NetID()); + if (p) + return p->SendMessage(msg); + } + + delete msg; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +NetMsg* +NetLink::GetMessage(DWORD netid) +{ + NetMsg* msg = 0; + + // receive from specific host: + if (netid) { + NetPeer* p = FindPeer(netid); + if (p) { + msg = p->GetMessage(); + + sync.acquire(); + recv_list.remove(msg); + sync.release(); + } + } + + return msg; +} + +// +--------------------------------------------------------------------+ + +NetMsg* +NetLink::GetMessage() +{ + NetMsg* msg = 0; + + // get first available packet: + + // Double-checked locking: + if (recv_list.size()) { + sync.acquire(); + if (recv_list.size()) { + msg = recv_list.removeIndex(0); + } + sync.release(); + + if (msg && msg->NetID()) { + NetPeer* p = FindPeer(msg->NetID()); + if (p) { + p->GetMessage(); // remove message from peer's list + // don't do this inside of sync block - + // it might cause a deadlock + } + } + } + + return msg; +} + +// +--------------------------------------------------------------------+ + +void +NetLink::Shutdown() +{ + shutdown = true; +} + +// +--------------------------------------------------------------------+ + +DWORD WINAPI NetLinkProc(LPVOID link) +{ + NetLink* netlink = (NetLink*) link; + + if (netlink) + return netlink->DoSendRecv(); + + return (DWORD) E_POINTER; +} + +DWORD +NetLink::DoSendRecv() +{ + while (!shutdown) { + ReadPackets(); + SendPackets(); + + // discard reeeeally old peers: + sync.acquire(); + + ListIter iter = peer_list; + while (!shutdown && ++iter) { + NetPeer* peer = iter.value(); + + if ((NetLayer::GetUTC() - peer->LastReceiveTime()) > 300) + delete iter.removeItem(); + } + + sync.release(); + Sleep(traffic_time); + } + + return 0; +} + +void +NetLink::ReadPackets() +{ + while (!shutdown && sock.select(NetSock::SELECT_READ) > 0) { + NetGram* gram = RecvNetGram(); + + if (gram && gram->IsReliable()) { + if (gram->IsAck()) { + ProcessAck(gram); + delete gram; + } + else { + AckNetGram(gram); + QueueNetGram(gram); + } + } + else { + QueueNetGram(gram); + } + } +} + +void +NetLink::SendPackets() +{ + if (shutdown) + return; + + if (sock.select(NetSock::SELECT_WRITE) > 0) { + DoRetries(); + } + + AutoThreadSync auto_sync(sync); + + ListIter iter = peer_list; + while (!shutdown && ++iter) { + NetPeer* p = iter.value(); + NetGram* g = 0; + + do { + if (sock.select(NetSock::SELECT_WRITE) > 0) { + g = p->ComposeGram(); + if (g) { + SendNetGram(g); + } + } + else { + g = 0; + } + } + while (!shutdown && g); + } +} + +// +--------------------------------------------------------------------+ + +void +NetLink::SendNetGram(NetGram* gram) +{ + if (gram) { + if (gram->IsReliable()) { + send_list.append(gram); + } + + int err = sock.sendto(gram->Body(), gram->Address()); + + if (err < 0) { + err = NetLayer::GetLastError(); + } + else { + packets_sent += 1; + bytes_sent += gram->Size() + UDP_HEADER_SIZE; + } + + if (!gram->IsReliable()) + delete gram; + } +} + +NetGram* +NetLink::RecvNetGram() +{ + NetAddr from; + Text msg = sock.recvfrom(&from); + + packets_recv += 1; + bytes_recv += msg.length() + UDP_HEADER_SIZE; + + return new(__FILE__, __LINE__) NetGram(from, msg); +} + +// +--------------------------------------------------------------------+ + +void +NetLink::AckNetGram(NetGram* gram) +{ + if (gram) { + NetGram ack = gram->Ack(); + + int err = sock.sendto(ack.Body(), gram->Address()); + if (err < 0) + err = NetLayer::GetLastError(); + } + else { + Print("NetLink::AckNetGram( NULL!!! )\n"); + } +} + +void +NetLink::ProcessAck(NetGram* gram) +{ + if (!shutdown && send_list.size()) { + AutoThreadSync auto_sync(sync); + + // remove the ack flag: + gram->ClearAck(); + + // find a matching outgoing packet: + int sent = send_list.index(gram); + if (sent >= 0) { + NetGram* orig = send_list.removeIndex(sent); + DWORD time = NetLayer::GetTime(); + DWORD msec = time - orig->SendTime(); + double dlag = 0.75 * lag + 0.25 * msec; + + if (lag_index >= 10) lag_index = 0; + lag_samples[lag_index++] = msec; + + NetPeer* peer = FindPeer(orig->Address()); + if (peer) + peer->SetLastReceiveTime(NetLayer::GetUTC()); + + delete orig; + + lag = (DWORD) dlag; + + if (lag > 100) + resend_time = 3 * lag; + else + resend_time = 300; + } + } +} + +void +NetLink::QueueNetGram(NetGram* gram) +{ + if (!shutdown) { + AutoThreadSync auto_sync(sync); + + DWORD sequence = 0; + NetPeer* peer = FindPeer(gram->Address()); + + if (peer) { + sequence = peer->Sequence(); + } + else { + peer = new(__FILE__, __LINE__) NetPeer(gram->Address(), base_netid++); + if (peer) + peer_list.append(peer); + } + + if (!gram->IsReliable()) { + if (gram->Sequence() < sequence) { // discard, too old + delete gram; + return; + } + } + + // sort this gram into the recv list(s) based on sequence: + if (peer) { + peer->ReceiveGram(gram, &recv_list); + } + } +} + +// +--------------------------------------------------------------------+ + +void +NetLink::DoRetries() +{ + if (!shutdown) { + AutoThreadSync auto_sync(sync); + + if (send_list.size()) { + int time = (int) NetLayer::GetTime(); + + ListIter iter = send_list; + while (!shutdown && ++iter) { + NetGram* gram = iter.value(); + + // still trying ? + if (gram->Retries() > 0) { + DWORD last_send = gram->SendTime(); + DWORD delta = time - last_send; + + if (delta > resend_time) { + gram->Retry(); + sock.sendto(gram->Body(), gram->Address()); + retries++; + } + } + + // oh, give it up: + else { + iter.removeItem(); + delete gram; + drops++; + } + } + } + } +} + +// +--------------------------------------------------------------------+ + +NetPeer* +NetLink::FindPeer(DWORD netid) +{ + AutoThreadSync auto_sync(sync); + NetPeer* peer = 0; + + ListIter iter = peer_list; + while (++iter && !peer) { + NetPeer* p = iter.value(); + + if (p->NetID() == netid) + peer = p; + } + + return peer; +} + +NetPeer* +NetLink::FindPeer(const NetAddr& a) +{ + AutoThreadSync auto_sync(sync); + NetPeer* peer = 0; + + ListIter iter = peer_list; + while (++iter && !peer) { + NetPeer* p = iter.value(); + + if (p->Address() == a) + peer = p; + } + + return peer; +} diff --git a/NetEx/NetLink.h b/NetEx/NetLink.h index 8d83ced..c39a32e 100644 --- a/NetEx/NetLink.h +++ b/NetEx/NetLink.h @@ -1,113 +1,137 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetLink.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Link for Remote Player -*/ - - -#ifndef NetLink_h -#define NetLink_h - -#include -#include "NetAddr.h" -#include "NetSock.h" -#include "List.h" -#include "ThreadSync.h" - -// +-------------------------------------------------------------------+ - -class NetGram; -class NetMsg; -class NetPeer; - -// +-------------------------------------------------------------------+ - -class NetLink -{ -public: - static const char* TYPENAME() { return "NetLink"; } - - NetLink(); - NetLink(NetAddr& a); - virtual ~NetLink(); - - int operator == (const NetLink& that) const { return this == &that; } - - const NetAddr& GetAddress() const { return addr; } - - DWORD AddPeer(const char* a, WORD p=12345); - DWORD AddPeer(DWORD a, WORD p=12345); - DWORD AddPeer(const NetAddr& a); - - bool SendMessage(DWORD nid, void* d, int l, BYTE f=0); - bool SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f=0); - bool SendMessage(NetMsg* msg); - - NetMsg* GetMessage(); - NetMsg* GetMessage(DWORD netid); - - virtual void Shutdown(); - DWORD DoSendRecv(); - - DWORD GetResendInterval() const { return resend_time; } - void SetResendInterval(DWORD t) { resend_time = t; } - DWORD GetTrafficInterval() const { return traffic_time; } - void SetTrafficInterval(DWORD t) { traffic_time = t; } - - DWORD GetPacketsSent() const { return packets_sent; } - DWORD GetPacketsRecv() const { return packets_recv; } - DWORD GetBytesSent() const { return bytes_sent; } - DWORD GetBytesRecv() const { return bytes_recv; } - DWORD GetRetries() const { return retries; } - DWORD GetDrops() const { return drops; } - DWORD GetLag() const { return lag; } - - NetPeer* FindPeer(const NetAddr& a); - NetPeer* FindPeer(DWORD netid); - -protected: - void SendNetGram(NetGram* g); - NetGram* RecvNetGram(); - void AckNetGram(NetGram* gram); - void ProcessAck(NetGram* gram); - void QueueNetGram(NetGram* gram); - - void ReadPackets(); - void SendPackets(); - void DoRetries(); - - NetAddr addr; - NetSock sock; - List send_list; - List recv_list; - List peer_list; - - HANDLE hnet; - bool shutdown; - ThreadSync sync; - - DWORD resend_time; - DWORD traffic_time; - - DWORD packets_sent; - DWORD packets_recv; - DWORD bytes_sent; - DWORD bytes_recv; - DWORD retries; - DWORD drops; - DWORD lag; - - DWORD lag_samples[10]; - int lag_index; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetLink.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Link for Remote Player +*/ + + +#ifndef NetLink_h +#define NetLink_h + +#include +#include "NetAddr.h" +#include "NetSock.h" +#include "List.h" +#include "ThreadSync.h" + +// +-------------------------------------------------------------------+ + +class NetGram; +class NetMsg; +class NetPeer; + +// +-------------------------------------------------------------------+ + +class NetLink +{ +public: + static const char* TYPENAME() { return "NetLink"; } + + NetLink(); + NetLink(NetAddr& a); + virtual ~NetLink(); + + int operator == (const NetLink& that) const { return this == &that; } + + const NetAddr& GetAddress() const { return addr; } + + DWORD AddPeer(const char* a, WORD p=12345); + DWORD AddPeer(DWORD a, WORD p=12345); + DWORD AddPeer(const NetAddr& a); + + bool SendMessage(DWORD nid, void* d, int l, BYTE f=0); + bool SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f=0); + bool SendMessage(NetMsg* msg); + + NetMsg* GetMessage(); + NetMsg* GetMessage(DWORD netid); + + virtual void Shutdown(); + DWORD DoSendRecv(); + + DWORD GetResendInterval() const { return resend_time; } + void SetResendInterval(DWORD t) { resend_time = t; } + DWORD GetTrafficInterval() const { return traffic_time; } + void SetTrafficInterval(DWORD t) { traffic_time = t; } + + DWORD GetPacketsSent() const { return packets_sent; } + DWORD GetPacketsRecv() const { return packets_recv; } + DWORD GetBytesSent() const { return bytes_sent; } + DWORD GetBytesRecv() const { return bytes_recv; } + DWORD GetRetries() const { return retries; } + DWORD GetDrops() const { return drops; } + DWORD GetLag() const { return lag; } + + NetPeer* FindPeer(const NetAddr& a); + NetPeer* FindPeer(DWORD netid); + +protected: + void SendNetGram(NetGram* g); + NetGram* RecvNetGram(); + void AckNetGram(NetGram* gram); + void ProcessAck(NetGram* gram); + void QueueNetGram(NetGram* gram); + + void ReadPackets(); + void SendPackets(); + void DoRetries(); + + NetAddr addr; + NetSock sock; + List send_list; + List recv_list; + List peer_list; + + HANDLE hnet; + bool shutdown; + ThreadSync sync; + + DWORD resend_time; + DWORD traffic_time; + + DWORD packets_sent; + DWORD packets_recv; + DWORD bytes_sent; + DWORD bytes_recv; + DWORD retries; + DWORD drops; + DWORD lag; + + DWORD lag_samples[10]; + int lag_index; +}; + + #endif NetLink_h \ No newline at end of file diff --git a/NetEx/NetMsg.cpp b/NetEx/NetMsg.cpp index dec0bfd..e326d3a 100644 --- a/NetEx/NetMsg.cpp +++ b/NetEx/NetMsg.cpp @@ -1,89 +1,113 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetMsg.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - User level network message -*/ - - -#include "MemDebug.h" -#include -#include "NetMsg.h" - -// +-------------------------------------------------------------------+ - -static DWORD net_msg_sequence = 1; - -// +-------------------------------------------------------------------+ - -NetMsg::NetMsg(DWORD nid, void* d, int l, BYTE f) - : msgid(net_msg_sequence++), netid(nid), len(l), flags(f) -{ - data = new(__FILE__,__LINE__) BYTE[len]; - - if (data) { - CopyMemory(data, d, len); - - if (len < MAX_SIZE) - data[1] = len; - else - data[1] = 0; - } - else { - len = 0; - } -} - -// +-------------------------------------------------------------------+ - -NetMsg::NetMsg(DWORD nid, BYTE type, const char* text, int l, BYTE f) - : msgid(net_msg_sequence++), netid(nid), len(2+l), flags(f) -{ - data = new(__FILE__,__LINE__) BYTE[len]; - - if (data) { - data[0] = type; - - if (len < MAX_SIZE) - data[1] = len; - else - data[1] = 0; - - if (len > 2) - CopyMemory(data+2, text, len-2); - } - else { - len = 0; - } -} - -// +-------------------------------------------------------------------+ - -NetMsg::~NetMsg() -{ - delete [] data; -} - -// +-------------------------------------------------------------------+ - -int NetMsg::operator < (const NetMsg& m) const -{ - if (data[0] == MULTIPART && m.data[0] == MULTIPART) { - NetMsgMultipart* p1 = (NetMsgMultipart*) data; - NetMsgMultipart* p2 = (NetMsgMultipart*) m.data; - - if (p1->msgid == p2->msgid) - return p1->partno < p2->partno; - - return p1->msgid < p2->msgid; - } - - return msgid < m.msgid; -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetMsg.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + User level network message +*/ + + +#include "MemDebug.h" +#include +#include "NetMsg.h" + +// +-------------------------------------------------------------------+ + +static DWORD net_msg_sequence = 1; + +// +-------------------------------------------------------------------+ + +NetMsg::NetMsg(DWORD nid, void* d, int l, BYTE f) + : msgid(net_msg_sequence++), netid(nid), len(l), flags(f) +{ + data = new(__FILE__,__LINE__) BYTE[len]; + + if (data) { + CopyMemory(data, d, len); + + if (len < MAX_SIZE) + data[1] = len; + else + data[1] = 0; + } + else { + len = 0; + } +} + +// +-------------------------------------------------------------------+ + +NetMsg::NetMsg(DWORD nid, BYTE type, const char* text, int l, BYTE f) + : msgid(net_msg_sequence++), netid(nid), len(2+l), flags(f) +{ + data = new(__FILE__,__LINE__) BYTE[len]; + + if (data) { + data[0] = type; + + if (len < MAX_SIZE) + data[1] = len; + else + data[1] = 0; + + if (len > 2) + CopyMemory(data+2, text, len-2); + } + else { + len = 0; + } +} + +// +-------------------------------------------------------------------+ + +NetMsg::~NetMsg() +{ + delete [] data; +} + +// +-------------------------------------------------------------------+ + +int NetMsg::operator < (const NetMsg& m) const +{ + if (data[0] == MULTIPART && m.data[0] == MULTIPART) { + NetMsgMultipart* p1 = (NetMsgMultipart*) data; + NetMsgMultipart* p2 = (NetMsgMultipart*) m.data; + + if (p1->msgid == p2->msgid) + return p1->partno < p2->partno; + + return p1->msgid < p2->msgid; + } + + return msgid < m.msgid; +} diff --git a/NetEx/NetMsg.h b/NetEx/NetMsg.h index 1c2a91a..072886b 100644 --- a/NetEx/NetMsg.h +++ b/NetEx/NetMsg.h @@ -1,81 +1,105 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetMsg.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - User level network message -*/ - - -#ifndef NetMsg_h -#define NetMsg_h - -#include -#include "NetAddr.h" -#include "NetGram.h" -#include "NetSock.h" -#include "List.h" - -// +-------------------------------------------------------------------+ - -class NetMsg -{ -public: - static const char* TYPENAME() { return "NetMsg"; } - - enum FLAGS { RELIABLE = 0x01, PRIORITY = 0x02, SCATTER = 0x04 }; - enum TYPES { INVALID = 0, - RESERVED = 0xF0, - MULTIPART = 0xF1 - }; - enum { MAX_SIZE = 250 }; - - NetMsg(DWORD nid, void* d, int l, BYTE f=0); - NetMsg(DWORD nid, BYTE type, const char* text, int len, BYTE f=0); - ~NetMsg(); - - int operator == (const NetMsg& m) const { return msgid == m.msgid && - netid == m.netid; } - int operator < (const NetMsg& m) const; - - DWORD Sequence() const { return msgid; } - DWORD NetID() const { return netid; } - const BYTE* Data() const { return data; } - BYTE Type() const { return data ? *data : 0; } - int Length() const { return len; } - BYTE Flags() const { return flags; } - - bool IsReliable() const { return flags & RELIABLE ? true : false; } - bool IsPriority() const { return flags & PRIORITY ? true : false; } - bool IsScatter() const { return flags & SCATTER ? true : false; } - - void SetSequence(DWORD s) { msgid = s; } - -private: - DWORD msgid; - DWORD netid; - BYTE* data; - int len; - BYTE flags; -}; - -// +-------------------------------------------------------------------+ - -struct NetMsgMultipart { - BYTE type; - BYTE len; - DWORD msgid; - DWORD partno; - DWORD nparts; - BYTE payload[256]; -}; - -// +-------------------------------------------------------------------+ - -#endif NetMsg_h \ No newline at end of file +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetMsg.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + User level network message +*/ + + +#ifndef NetMsg_h +#define NetMsg_h + +#include +#include "NetAddr.h" +#include "NetGram.h" +#include "NetSock.h" +#include "List.h" + +// +-------------------------------------------------------------------+ + +class NetMsg +{ +public: + static const char* TYPENAME() { return "NetMsg"; } + + enum FLAGS { RELIABLE = 0x01, PRIORITY = 0x02, SCATTER = 0x04 }; + enum TYPES { INVALID = 0, + RESERVED = 0xF0, + MULTIPART = 0xF1 + }; + enum { MAX_SIZE = 250 }; + + NetMsg(DWORD nid, void* d, int l, BYTE f=0); + NetMsg(DWORD nid, BYTE type, const char* text, int len, BYTE f=0); + ~NetMsg(); + + int operator == (const NetMsg& m) const { return msgid == m.msgid && + netid == m.netid; } + int operator < (const NetMsg& m) const; + + DWORD Sequence() const { return msgid; } + DWORD NetID() const { return netid; } + const BYTE* Data() const { return data; } + BYTE Type() const { return data ? *data : 0; } + int Length() const { return len; } + BYTE Flags() const { return flags; } + + bool IsReliable() const { return flags & RELIABLE ? true : false; } + bool IsPriority() const { return flags & PRIORITY ? true : false; } + bool IsScatter() const { return flags & SCATTER ? true : false; } + + void SetSequence(DWORD s) { msgid = s; } + +private: + DWORD msgid; + DWORD netid; + BYTE* data; + int len; + BYTE flags; +}; + +// +-------------------------------------------------------------------+ + +struct NetMsgMultipart { + BYTE type; + BYTE len; + DWORD msgid; + DWORD partno; + DWORD nparts; + BYTE payload[256]; +}; + +// +-------------------------------------------------------------------+ + +#endif NetMsg_h diff --git a/NetEx/NetPeer.cpp b/NetEx/NetPeer.cpp index 6ead8a4..94f68a5 100644 --- a/NetEx/NetPeer.cpp +++ b/NetEx/NetPeer.cpp @@ -1,440 +1,464 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetPeer.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - One side of a UDP net link connection -*/ - - -#include "MemDebug.h" -#include -#include "NetPeer.h" -#include "NetGram.h" -#include "NetMsg.h" -#include "NetLayer.h" - -#include - -// +-------------------------------------------------------------------+ - -const int MULTIPART_CHUNKSIZE = 232; -const int MULTIPART_HEADER = 16; -const int UDP_HEADER_SIZE = 34; - -static NetMsgMultipart multi_part_buffer; -static DWORD multi_msg_sequence = 1; - -// +-------------------------------------------------------------------+ - -NetPeer::NetPeer(const NetAddr& a, DWORD id) - : addr(a), netid(id), sequence(0), pps(0), bps(0), max_qsize(0), - status(OK), hist_indx(0), send_size(0), recv_size(0), - chunk_size(MULTIPART_CHUNKSIZE) -{ - ZeroMemory(hist_time, sizeof(hist_time)); - ZeroMemory(hist_size, sizeof(hist_size)); - - last_recv_time = NetLayer::GetUTC(); -} - -NetPeer::~NetPeer() -{ - send_list.destroy(); - recv_list.destroy(); - - multi_send_list.destroy(); - multi_recv_list.destroy(); -} - -// +-------------------------------------------------------------------+ - -bool -NetPeer::SendMessage(NetMsg* msg) -{ - if (msg) { - if (max_qsize > 0 && msg->Length() + send_size > max_qsize) { - status = SEND_OVERFLOW; - delete msg; - return false; - } - - // simple message - if (msg->Length() <= (int) chunk_size) { - if (msg->IsPriority()) - send_list.insert(msg); - else - send_list.append(msg); - - send_size += msg->Length(); - } - - // multipart message - else { - List* list = &send_list; - - if (msg->IsScatter()) - list = &multi_send_list; - - DWORD nparts = msg->Length() / chunk_size; - DWORD extra = msg->Length() % chunk_size; - - if (extra > 0) nparts++; - - multi_part_buffer.type = NetMsg::MULTIPART; - multi_part_buffer.msgid = multi_msg_sequence++; - multi_part_buffer.nparts = nparts; - - DWORD header_size = (DWORD) (&multi_part_buffer.payload) - - (DWORD) (&multi_part_buffer); - - const BYTE* p = msg->Data(); - - for (DWORD i = 0; i < nparts; i++) { - multi_part_buffer.partno = i; - NetMsg* part = 0; - DWORD part_size = chunk_size; - - if (i == nparts-1 && extra > 0) // last partial payload - part_size = extra; - - CopyMemory(multi_part_buffer.payload, p, part_size); - p += part_size; - part = new(__FILE__,__LINE__) NetMsg(msg->NetID(), - &multi_part_buffer, - header_size + part_size, - msg->Flags()); - - if (part) { - list->append(part); - send_size += part->Length(); - } - } - } - - return true; - } - - return false; -} - -// +-------------------------------------------------------------------+ - -NetMsg* -NetPeer::GetMessage() -{ - if (recv_list.size() > 0) { - NetMsg* msg = recv_list.removeIndex(0); - recv_size -= msg->Length(); - return msg; - } - - return 0; -} - -// +-------------------------------------------------------------------+ - -NetGram* -NetPeer::ComposeGram() -{ - NetGram* g = 0; - - if ((send_list.size() || multi_send_list.size()) && OKtoSend()) { - AutoThreadSync auto_sync(sync); - - int xmit_size = send_size; - int nmsg = send_list.size(); - int limit = NET_GRAM_MAX_SIZE; - bool reliable = false; - bool is_multi = false; - - NetMsg* multi_msg = 0; - List* list = &send_list; - - if (xmit_size > limit) { - xmit_size = 0; - nmsg = 0; - - if (send_list.size() > 0) { - - // if there is regular traffic, and multipart traffic - if (multi_send_list.size()) { - // just send one multipart message in this packet - multi_msg = multi_send_list.removeIndex(0); - limit -= multi_msg->Length(); - reliable = true; - is_multi = true; - } - - for (int i = 0; i < send_list.size(); i++) { - NetMsg* msg = send_list[i]; - - if (xmit_size + msg->Length() < limit) { - xmit_size += msg->Length(); - nmsg++; - } - else { - break; - } - } - } - else { - // if there is only multipart traffic, - // send as many multipart messages as will fit: - list = &multi_send_list; - reliable = true; - is_multi = true; - - for (int i = 0; i < multi_send_list.size(); i++) { - NetMsg* msg = multi_send_list[i]; - - if (xmit_size + msg->Length() < limit) { - xmit_size += msg->Length(); - nmsg++; - } - else { - break; - } - } - } - } - - if (xmit_size > 0 && nmsg > 0) { - BYTE* buffer = new(__FILE__,__LINE__) BYTE[xmit_size]; - BYTE* p = buffer; - - if (multi_msg) { - if (buffer) { - CopyMemory(p, multi_msg->Data(), multi_msg->Length()); - p[1] = multi_msg->Length(); - p += multi_msg->Length(); - } - delete multi_msg; - } - - while (nmsg-- && p < buffer + xmit_size) { - NetMsg* msg = list->removeIndex(0); - - if (msg) { - if (msg->IsReliable()) reliable = true; - if (buffer) { - CopyMemory(p, msg->Data(), msg->Length()); - p[1] = msg->Length(); - p += msg->Length(); - } - delete msg; - } - } - - if (buffer) { - Text user_data((const char*) buffer, xmit_size); - int retries = 0; - - if (reliable) - retries = 5; - - if (is_multi) - retries = 10; - - send_size -= xmit_size; - - hist_size[hist_indx] = xmit_size + UDP_HEADER_SIZE; - hist_time[hist_indx] = NetLayer::GetTime(); - hist_indx++; - - if (hist_indx >= HIST_SIZE) - hist_indx = 0; - - g = new(__FILE__,__LINE__) NetGram(addr, user_data, retries); - delete[] buffer; - } - } - - // the next msg is too big to fit in a single packet - else { - NetMsg* m = send_list.removeIndex(0); - send_size -= m->Length(); - delete m; - } - } - - return g; -} - -// +-------------------------------------------------------------------+ - -bool -NetPeer::ReceiveGram(NetGram* g, List* q) -{ - if (g) { - if (max_qsize > 0 && recv_size + g->Size() > max_qsize) { - status = RECV_OVERFLOW; - delete g; - return false; - } - - sequence = g->Sequence(); - recv_size += g->Size() - NET_GRAM_HEADER_SIZE; - - // PARSE THE BLOCKS: - BYTE* p = g->UserData(); - - while (p < g->Data() + g->Size()) { - BYTE block_type = p[0]; - BYTE block_size = p[1]; - - if (!block_type || !block_size) - break; - - NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, p, block_size); - - if (msg) { - if (msg->Type() < NetMsg::RESERVED) { - msg->SetSequence(sequence); - - recv_list.insertSort(msg); - - if (q) - q->insertSort(msg); - - p += block_size; - } - - else if (msg->Type() == NetMsg::MULTIPART) { - multi_recv_list.insertSort(msg); - p += block_size; - - CheckMultiRecv(q); - } - } - } - - last_recv_time = NetLayer::GetUTC(); - - delete g; - return true; - } - - return false; -} - -// +-------------------------------------------------------------------+ - -bool -NetPeer::OKtoSend() const -{ - if (pps || bps) { - DWORD hist_total = 0; - DWORD hist_count = 0; - DWORD now = NetLayer::GetTime(); - DWORD hist_oldest = now; - DWORD hist_newest = 0; - - for (int i = 0; i < HIST_SIZE; i++) { - if (hist_size[i] > 0) { - hist_total += hist_size[i]; - hist_count++; - } - - if (hist_time[i] > 0) { - if (hist_time[i] < hist_oldest) - hist_oldest = hist_time[i]; - - if (hist_time[i] > hist_newest) - hist_newest = hist_time[i]; - } - } - - if (now - hist_newest < (DWORD) pps) - return false; - - DWORD delta = now - hist_oldest; - DWORD avg_bps = hist_total / delta; - - if (bps > 0 && avg_bps > (DWORD) bps) - return false; - } - - return true; -} - -// +-------------------------------------------------------------------+ - -struct PacketAssembly { - DWORD msgid; - DWORD netid; - int nreq; - int nparts; - int nbytes; -}; - -void -NetPeer::CheckMultiRecv(List* q) -{ - const int MAX_SIMULTANEOUS_MULTI_SEQUENCES = 8; - - PacketAssembly assy[MAX_SIMULTANEOUS_MULTI_SEQUENCES]; - ZeroMemory(assy, sizeof(assy)); - - DWORD header_size = (DWORD) (&multi_part_buffer.payload) - - (DWORD) (&multi_part_buffer); - - // Catalog how much of each multipart sequence has been received: - for (int i = 0; i < multi_recv_list.size(); i++) { - NetMsg* msg = multi_recv_list[i]; - NetMsgMultipart* m = (NetMsgMultipart*) msg->Data(); - - for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) { - PacketAssembly* a = assy + n; - - if (a->msgid == 0 || (a->msgid == m->msgid && a->netid == msg->NetID())) { - a->msgid = m->msgid; - a->netid = msg->NetID(); - a->nreq = m->nparts; - a->nparts += 1; - a->nbytes += m->len - header_size; - break; - } - } - } - - for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) { - PacketAssembly* a = assy + n; - - // is this sequence complete? - if (a->msgid && a->nparts == a->nreq) { - BYTE* buffer = new BYTE[a->nbytes]; - BYTE* p = buffer; - WORD nid = 0; - - ListIter iter = multi_recv_list; - while (++iter) { - netid = iter->NetID(); - NetMsgMultipart* m = (NetMsgMultipart*) iter->Data(); - - // found part of the sequence - if (m->msgid == a->msgid && netid == a->netid) { - // copy it into the buffer - CopyMemory(p, m->payload, m->len - header_size); - p += m->len - header_size; - - delete iter.removeItem(); - } - } - - NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, buffer, a->nbytes, NetMsg::RELIABLE); - if (msg) { - recv_list.insertSort(msg); - - if (q) - q->insertSort(msg); - } - } - } -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetPeer.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + One side of a UDP net link connection +*/ + + +#include "MemDebug.h" +#include +#include "NetPeer.h" +#include "NetGram.h" +#include "NetMsg.h" +#include "NetLayer.h" + +#include + +// +-------------------------------------------------------------------+ + +const int MULTIPART_CHUNKSIZE = 232; +const int MULTIPART_HEADER = 16; +const int UDP_HEADER_SIZE = 34; + +static NetMsgMultipart multi_part_buffer; +static DWORD multi_msg_sequence = 1; + +// +-------------------------------------------------------------------+ + +NetPeer::NetPeer(const NetAddr& a, DWORD id) + : addr(a), netid(id), sequence(0), pps(0), bps(0), max_qsize(0), + status(OK), hist_indx(0), send_size(0), recv_size(0), + chunk_size(MULTIPART_CHUNKSIZE) +{ + ZeroMemory(hist_time, sizeof(hist_time)); + ZeroMemory(hist_size, sizeof(hist_size)); + + last_recv_time = NetLayer::GetUTC(); +} + +NetPeer::~NetPeer() +{ + send_list.destroy(); + recv_list.destroy(); + + multi_send_list.destroy(); + multi_recv_list.destroy(); +} + +// +-------------------------------------------------------------------+ + +bool +NetPeer::SendMessage(NetMsg* msg) +{ + if (msg) { + if (max_qsize > 0 && msg->Length() + send_size > max_qsize) { + status = SEND_OVERFLOW; + delete msg; + return false; + } + + // simple message + if (msg->Length() <= (int) chunk_size) { + if (msg->IsPriority()) + send_list.insert(msg); + else + send_list.append(msg); + + send_size += msg->Length(); + } + + // multipart message + else { + List* list = &send_list; + + if (msg->IsScatter()) + list = &multi_send_list; + + DWORD nparts = msg->Length() / chunk_size; + DWORD extra = msg->Length() % chunk_size; + + if (extra > 0) nparts++; + + multi_part_buffer.type = NetMsg::MULTIPART; + multi_part_buffer.msgid = multi_msg_sequence++; + multi_part_buffer.nparts = nparts; + + DWORD header_size = (DWORD) (&multi_part_buffer.payload) - + (DWORD) (&multi_part_buffer); + + const BYTE* p = msg->Data(); + + for (DWORD i = 0; i < nparts; i++) { + multi_part_buffer.partno = i; + NetMsg* part = 0; + DWORD part_size = chunk_size; + + if (i == nparts-1 && extra > 0) // last partial payload + part_size = extra; + + CopyMemory(multi_part_buffer.payload, p, part_size); + p += part_size; + part = new(__FILE__,__LINE__) NetMsg(msg->NetID(), + &multi_part_buffer, + header_size + part_size, + msg->Flags()); + + if (part) { + list->append(part); + send_size += part->Length(); + } + } + } + + return true; + } + + return false; +} + +// +-------------------------------------------------------------------+ + +NetMsg* +NetPeer::GetMessage() +{ + if (recv_list.size() > 0) { + NetMsg* msg = recv_list.removeIndex(0); + recv_size -= msg->Length(); + return msg; + } + + return 0; +} + +// +-------------------------------------------------------------------+ + +NetGram* +NetPeer::ComposeGram() +{ + NetGram* g = 0; + + if ((send_list.size() || multi_send_list.size()) && OKtoSend()) { + AutoThreadSync auto_sync(sync); + + int xmit_size = send_size; + int nmsg = send_list.size(); + int limit = NET_GRAM_MAX_SIZE; + bool reliable = false; + bool is_multi = false; + + NetMsg* multi_msg = 0; + List* list = &send_list; + + if (xmit_size > limit) { + xmit_size = 0; + nmsg = 0; + + if (send_list.size() > 0) { + + // if there is regular traffic, and multipart traffic + if (multi_send_list.size()) { + // just send one multipart message in this packet + multi_msg = multi_send_list.removeIndex(0); + limit -= multi_msg->Length(); + reliable = true; + is_multi = true; + } + + for (int i = 0; i < send_list.size(); i++) { + NetMsg* msg = send_list[i]; + + if (xmit_size + msg->Length() < limit) { + xmit_size += msg->Length(); + nmsg++; + } + else { + break; + } + } + } + else { + // if there is only multipart traffic, + // send as many multipart messages as will fit: + list = &multi_send_list; + reliable = true; + is_multi = true; + + for (int i = 0; i < multi_send_list.size(); i++) { + NetMsg* msg = multi_send_list[i]; + + if (xmit_size + msg->Length() < limit) { + xmit_size += msg->Length(); + nmsg++; + } + else { + break; + } + } + } + } + + if (xmit_size > 0 && nmsg > 0) { + BYTE* buffer = new(__FILE__,__LINE__) BYTE[xmit_size]; + BYTE* p = buffer; + + if (multi_msg) { + if (buffer) { + CopyMemory(p, multi_msg->Data(), multi_msg->Length()); + p[1] = multi_msg->Length(); + p += multi_msg->Length(); + } + delete multi_msg; + } + + while (nmsg-- && p < buffer + xmit_size) { + NetMsg* msg = list->removeIndex(0); + + if (msg) { + if (msg->IsReliable()) reliable = true; + if (buffer) { + CopyMemory(p, msg->Data(), msg->Length()); + p[1] = msg->Length(); + p += msg->Length(); + } + delete msg; + } + } + + if (buffer) { + Text user_data((const char*) buffer, xmit_size); + int retries = 0; + + if (reliable) + retries = 5; + + if (is_multi) + retries = 10; + + send_size -= xmit_size; + + hist_size[hist_indx] = xmit_size + UDP_HEADER_SIZE; + hist_time[hist_indx] = NetLayer::GetTime(); + hist_indx++; + + if (hist_indx >= HIST_SIZE) + hist_indx = 0; + + g = new(__FILE__,__LINE__) NetGram(addr, user_data, retries); + delete[] buffer; + } + } + + // the next msg is too big to fit in a single packet + else { + NetMsg* m = send_list.removeIndex(0); + send_size -= m->Length(); + delete m; + } + } + + return g; +} + +// +-------------------------------------------------------------------+ + +bool +NetPeer::ReceiveGram(NetGram* g, List* q) +{ + if (g) { + if (max_qsize > 0 && recv_size + g->Size() > max_qsize) { + status = RECV_OVERFLOW; + delete g; + return false; + } + + sequence = g->Sequence(); + recv_size += g->Size() - NET_GRAM_HEADER_SIZE; + + // PARSE THE BLOCKS: + BYTE* p = g->UserData(); + + while (p < g->Data() + g->Size()) { + BYTE block_type = p[0]; + BYTE block_size = p[1]; + + if (!block_type || !block_size) + break; + + NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, p, block_size); + + if (msg) { + if (msg->Type() < NetMsg::RESERVED) { + msg->SetSequence(sequence); + + recv_list.insertSort(msg); + + if (q) + q->insertSort(msg); + + p += block_size; + } + + else if (msg->Type() == NetMsg::MULTIPART) { + multi_recv_list.insertSort(msg); + p += block_size; + + CheckMultiRecv(q); + } + } + } + + last_recv_time = NetLayer::GetUTC(); + + delete g; + return true; + } + + return false; +} + +// +-------------------------------------------------------------------+ + +bool +NetPeer::OKtoSend() const +{ + if (pps || bps) { + DWORD hist_total = 0; + DWORD hist_count = 0; + DWORD now = NetLayer::GetTime(); + DWORD hist_oldest = now; + DWORD hist_newest = 0; + + for (int i = 0; i < HIST_SIZE; i++) { + if (hist_size[i] > 0) { + hist_total += hist_size[i]; + hist_count++; + } + + if (hist_time[i] > 0) { + if (hist_time[i] < hist_oldest) + hist_oldest = hist_time[i]; + + if (hist_time[i] > hist_newest) + hist_newest = hist_time[i]; + } + } + + if (now - hist_newest < (DWORD) pps) + return false; + + DWORD delta = now - hist_oldest; + DWORD avg_bps = hist_total / delta; + + if (bps > 0 && avg_bps > (DWORD) bps) + return false; + } + + return true; +} + +// +-------------------------------------------------------------------+ + +struct PacketAssembly { + DWORD msgid; + DWORD netid; + int nreq; + int nparts; + int nbytes; +}; + +void +NetPeer::CheckMultiRecv(List* q) +{ + const int MAX_SIMULTANEOUS_MULTI_SEQUENCES = 8; + + PacketAssembly assy[MAX_SIMULTANEOUS_MULTI_SEQUENCES]; + ZeroMemory(assy, sizeof(assy)); + + DWORD header_size = (DWORD) (&multi_part_buffer.payload) - + (DWORD) (&multi_part_buffer); + + // Catalog how much of each multipart sequence has been received: + for (int i = 0; i < multi_recv_list.size(); i++) { + NetMsg* msg = multi_recv_list[i]; + NetMsgMultipart* m = (NetMsgMultipart*) msg->Data(); + + for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) { + PacketAssembly* a = assy + n; + + if (a->msgid == 0 || (a->msgid == m->msgid && a->netid == msg->NetID())) { + a->msgid = m->msgid; + a->netid = msg->NetID(); + a->nreq = m->nparts; + a->nparts += 1; + a->nbytes += m->len - header_size; + break; + } + } + } + + for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) { + PacketAssembly* a = assy + n; + + // is this sequence complete? + if (a->msgid && a->nparts == a->nreq) { + BYTE* buffer = new BYTE[a->nbytes]; + BYTE* p = buffer; + WORD nid = 0; + + ListIter iter = multi_recv_list; + while (++iter) { + netid = iter->NetID(); + NetMsgMultipart* m = (NetMsgMultipart*) iter->Data(); + + // found part of the sequence + if (m->msgid == a->msgid && netid == a->netid) { + // copy it into the buffer + CopyMemory(p, m->payload, m->len - header_size); + p += m->len - header_size; + + delete iter.removeItem(); + } + } + + NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, buffer, a->nbytes, NetMsg::RELIABLE); + if (msg) { + recv_list.insertSort(msg); + + if (q) + q->insertSort(msg); + } + } + } +} diff --git a/NetEx/NetPeer.h b/NetEx/NetPeer.h index b2ec882..4849f2f 100644 --- a/NetEx/NetPeer.h +++ b/NetEx/NetPeer.h @@ -1,98 +1,122 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetPeer.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - One side of a UDP net link connection -*/ - - -#ifndef NetPeer_h -#define NetPeer_h - -#include -#include "NetAddr.h" -#include "List.h" -#include "ThreadSync.h" - -// +-------------------------------------------------------------------+ - -class NetGram; -class NetMsg; - -// +-------------------------------------------------------------------+ - -class NetPeer -{ -public: - static const char* TYPENAME() { return "NetPeer"; } - - enum STATUS { OK, SEND_OVERFLOW, RECV_OVERFLOW }; - - NetPeer(const NetAddr& addr, DWORD id); - ~NetPeer(); - - int operator == (const NetPeer& p) const { return netid == p.netid; } - - bool SendMessage(NetMsg* msg); - NetMsg* GetMessage(); - - NetGram* ComposeGram(); - bool ReceiveGram(NetGram* g, List* q=0); - - const NetAddr& Address() const { return addr; } - DWORD NetID() const { return netid; } - DWORD Sequence() const { return sequence; } - - int GetMaxPPS() const { return pps; } - void SetMaxPPS(int p) { pps = p; } - int GetMaxBPS() const { return bps; } - void SetMaxBPS(int b) { bps = b; } - int GetMaxQSize() const { return max_qsize; } - void SetMaxQSize(int q) { max_qsize = q; } - - DWORD GetChunkSize() const { return chunk_size; } - void SetChunkSize(DWORD s) { chunk_size = s; } - - DWORD LastReceiveTime() const { return last_recv_time; } - void SetLastReceiveTime(DWORD t) { last_recv_time = t; } - -private: - bool OKtoSend() const; - void CheckMultiRecv(List* q); - - NetAddr addr; // remote network address - DWORD sequence; // highest packet id received - DWORD netid; // unique id for this peer - int pps; // max packets per second - int bps; // max bits per second - int max_qsize; // max bytes in either queue - int status; // ok or error code - DWORD chunk_size; // size of multipart message chunk - - enum HIST { HIST_SIZE=8 }; - - DWORD last_recv_time; // time of last received packet - DWORD hist_time[HIST_SIZE]; // history for pps check - DWORD hist_size[HIST_SIZE]; // history for bps check - int hist_indx; // index into history - - int send_size; // total bytes in send list - int recv_size; // total bytes in recv list - List send_list; // queue of messages waiting to be sent - List recv_list; // queue of messages waiting to be read - - List multi_send_list; - List multi_recv_list; - - ThreadSync sync; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetPeer.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + One side of a UDP net link connection +*/ + + +#ifndef NetPeer_h +#define NetPeer_h + +#include +#include "NetAddr.h" +#include "List.h" +#include "ThreadSync.h" + +// +-------------------------------------------------------------------+ + +class NetGram; +class NetMsg; + +// +-------------------------------------------------------------------+ + +class NetPeer +{ +public: + static const char* TYPENAME() { return "NetPeer"; } + + enum STATUS { OK, SEND_OVERFLOW, RECV_OVERFLOW }; + + NetPeer(const NetAddr& addr, DWORD id); + ~NetPeer(); + + int operator == (const NetPeer& p) const { return netid == p.netid; } + + bool SendMessage(NetMsg* msg); + NetMsg* GetMessage(); + + NetGram* ComposeGram(); + bool ReceiveGram(NetGram* g, List* q=0); + + const NetAddr& Address() const { return addr; } + DWORD NetID() const { return netid; } + DWORD Sequence() const { return sequence; } + + int GetMaxPPS() const { return pps; } + void SetMaxPPS(int p) { pps = p; } + int GetMaxBPS() const { return bps; } + void SetMaxBPS(int b) { bps = b; } + int GetMaxQSize() const { return max_qsize; } + void SetMaxQSize(int q) { max_qsize = q; } + + DWORD GetChunkSize() const { return chunk_size; } + void SetChunkSize(DWORD s) { chunk_size = s; } + + DWORD LastReceiveTime() const { return last_recv_time; } + void SetLastReceiveTime(DWORD t) { last_recv_time = t; } + +private: + bool OKtoSend() const; + void CheckMultiRecv(List* q); + + NetAddr addr; // remote network address + DWORD sequence; // highest packet id received + DWORD netid; // unique id for this peer + int pps; // max packets per second + int bps; // max bits per second + int max_qsize; // max bytes in either queue + int status; // ok or error code + DWORD chunk_size; // size of multipart message chunk + + enum HIST { HIST_SIZE=8 }; + + DWORD last_recv_time; // time of last received packet + DWORD hist_time[HIST_SIZE]; // history for pps check + DWORD hist_size[HIST_SIZE]; // history for bps check + int hist_indx; // index into history + + int send_size; // total bytes in send list + int recv_size; // total bytes in recv list + List send_list; // queue of messages waiting to be sent + List recv_list; // queue of messages waiting to be read + + List multi_send_list; + List multi_recv_list; + + ThreadSync sync; +}; + + #endif NetPeer_h \ No newline at end of file diff --git a/NetEx/NetServer.cpp b/NetEx/NetServer.cpp index 6534202..1ec45fd 100644 --- a/NetEx/NetServer.cpp +++ b/NetEx/NetServer.cpp @@ -1,260 +1,284 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetServer.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#include "MemDebug.h" -#include "NetServer.h" -#include "NetHost.h" -#include "NetLayer.h" - -#include -#include -#include - -// +-------------------------------------------------------------------+ - -DWORD WINAPI NetServerListenerProc(LPVOID link); -DWORD WINAPI NetServerReaderProc(LPVOID link); - -struct PoolItem { NetServer* server; int thread_index; }; - -// +-------------------------------------------------------------------+ - -NetServer::NetServer(WORD port, int nthreads) - : sock(true), pool(0), conn(0), poolsize(nthreads), err(0), - server_shutdown(false), hreader(0) -{ - NetHost host; - addr = NetAddr(host.Address().IPAddr(), port); - - sock.bind(addr); - sock.listen(3); - - if (poolsize < 1) poolsize = 1; - - pool = new(__FILE__,__LINE__) HANDLE[poolsize]; - conn = new(__FILE__,__LINE__) NetSock*[poolsize]; - clients = new(__FILE__,__LINE__) NetAddr[poolsize]; - - if (pool && conn && clients) { - ZeroMemory(pool, poolsize * sizeof(HANDLE)); - ZeroMemory(conn, poolsize * sizeof(NetSock*)); - - DWORD thread_id = 0; - - for (int i = 0; i < poolsize; i++) { - thread_id = 0; - PoolItem* item = new PoolItem; - item->server = this; - item->thread_index = i; - - pool[i] = CreateThread(0, 4096, NetServerReaderProc, (LPVOID) item, 0, &thread_id); - } - - thread_id = 0; - hreader = CreateThread(0, 4096, NetServerListenerProc, (LPVOID) this, 0, &thread_id); - } -} - -NetServer::~NetServer() -{ - if (!server_shutdown) { - server_shutdown = true; - sock.close(); - } - - if (hreader) { - WaitForSingleObject(hreader, 1000); - CloseHandle(hreader); - } - - if (pool && poolsize) { - for (int i = 0; i < poolsize; i++) { - WaitForSingleObject(pool[i], 1000); - CloseHandle(pool[i]); - delete conn[i]; - conn[i] = 0; - } - - delete [] pool; - delete [] conn; - delete [] clients; - } -} - -// +--------------------------------------------------------------------+ - -void -NetServer::Shutdown() -{ - server_shutdown = true; -} - -// +--------------------------------------------------------------------+ - -DWORD WINAPI NetServerListenerProc(LPVOID link) -{ - NetServer* net_server = (NetServer*) link; - - if (net_server) - return net_server->Listener(); - - return (DWORD) E_POINTER; -} - -DWORD -NetServer::Listener() -{ - while (!server_shutdown) { - NetSock* s = sock.accept(&client_addr); - - while (s) { - sync.acquire(); - - for (int i = 0; i < poolsize; i++) { - if (conn[i] == 0) { - conn[i] = s; - clients[i] = client_addr; - s = 0; - break; - } - } - - sync.release(); - - // wait for a thread to become not busy - if (s) - Sleep(10); - } - } - - return 0; -} - -// +--------------------------------------------------------------------+ - -DWORD WINAPI NetServerReaderProc(LPVOID link) -{ - if (!link) return (DWORD) E_POINTER; - - PoolItem* item = (PoolItem*) link; - NetServer* net_server = item->server; - int index = item->thread_index; - - delete item; - - if (net_server) - return net_server->Reader(index); - - return (DWORD) E_POINTER; -} - -DWORD -NetServer::Reader(int index) -{ - // init random seed for this thread: - srand(timeGetTime()); - - while (!server_shutdown) { - sync.acquire(); - NetSock* s = conn[index]; - sync.release(); - - if (s) { - const int MAX_REQUEST = 4096; - Text request; - - /*** - *** NOT SURE WHY, BUT THIS DOESN'T WORK FOR SHIT - *** - *** Setting the socket timeout to 2 seconds caused it - *** to wait for two seconds, read nothing, and give up - *** with a WSAETIMEDOUT error. Meanwhile, the client - *** immediately registered a failure (during the 2 sec - *** delay) and aborted the request. - *** - - s->set_timeout(2000); - Text msg = s->recv(); - - while (msg.length() > 0 && request.length() < MAX_REQUEST) { - request += msg; - msg = s->recv(); - } - - ***/ - - request = s->recv(); - - if (request.length() > 0 && !s->is_closed()) { - Text response = ProcessRequest(request, clients[index]); - err = s->send(response); - if (err < 0) { - err = NetLayer::GetLastError(); - } - } - - sync.acquire(); - delete conn[index]; - conn[index] = 0; - sync.release(); - } - else { - Sleep(5); - } - } - - return 0; -} - -// +--------------------------------------------------------------------+ - -Text -NetServer::ProcessRequest(Text msg, const NetAddr& addr) -{ - if (msg.indexOf("GET ") == 0) - return DefaultResponse(); - - return ErrorResponse(); -} - -Text -NetServer::DefaultResponse() -{ - Text response = - "HTTP/1.0 200 OK\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n"; - - response += "Generic NetServer 1.0\n\n"; - response += "\n"; - response += "

Generic NetServer 1.0

\n"; - response += "

Didn't think I could do it, did ya?\n"; - response += "\n\n"; - - return response; -} - -Text -NetServer::ErrorResponse() -{ - Text response = - "HTTP/1.0 501 Not Implemented\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n"; - - response += "Generic NetServer 1.0\n\n"; - response += "\n"; - response += "

Generic NetServer 1.0

\n"; - response += "

Sorry charlie... I'm not a magician.\n"; - response += "\n\n"; - - return response; -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetServer.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#include "MemDebug.h" +#include "NetServer.h" +#include "NetHost.h" +#include "NetLayer.h" + +#include +#include +#include + +// +-------------------------------------------------------------------+ + +DWORD WINAPI NetServerListenerProc(LPVOID link); +DWORD WINAPI NetServerReaderProc(LPVOID link); + +struct PoolItem { NetServer* server; int thread_index; }; + +// +-------------------------------------------------------------------+ + +NetServer::NetServer(WORD port, int nthreads) + : sock(true), pool(0), conn(0), poolsize(nthreads), err(0), + server_shutdown(false), hreader(0) +{ + NetHost host; + addr = NetAddr(host.Address().IPAddr(), port); + + sock.bind(addr); + sock.listen(3); + + if (poolsize < 1) poolsize = 1; + + pool = new(__FILE__,__LINE__) HANDLE[poolsize]; + conn = new(__FILE__,__LINE__) NetSock*[poolsize]; + clients = new(__FILE__,__LINE__) NetAddr[poolsize]; + + if (pool && conn && clients) { + ZeroMemory(pool, poolsize * sizeof(HANDLE)); + ZeroMemory(conn, poolsize * sizeof(NetSock*)); + + DWORD thread_id = 0; + + for (int i = 0; i < poolsize; i++) { + thread_id = 0; + PoolItem* item = new PoolItem; + item->server = this; + item->thread_index = i; + + pool[i] = CreateThread(0, 4096, NetServerReaderProc, (LPVOID) item, 0, &thread_id); + } + + thread_id = 0; + hreader = CreateThread(0, 4096, NetServerListenerProc, (LPVOID) this, 0, &thread_id); + } +} + +NetServer::~NetServer() +{ + if (!server_shutdown) { + server_shutdown = true; + sock.close(); + } + + if (hreader) { + WaitForSingleObject(hreader, 1000); + CloseHandle(hreader); + } + + if (pool && poolsize) { + for (int i = 0; i < poolsize; i++) { + WaitForSingleObject(pool[i], 1000); + CloseHandle(pool[i]); + delete conn[i]; + conn[i] = 0; + } + + delete [] pool; + delete [] conn; + delete [] clients; + } +} + +// +--------------------------------------------------------------------+ + +void +NetServer::Shutdown() +{ + server_shutdown = true; +} + +// +--------------------------------------------------------------------+ + +DWORD WINAPI NetServerListenerProc(LPVOID link) +{ + NetServer* net_server = (NetServer*) link; + + if (net_server) + return net_server->Listener(); + + return (DWORD) E_POINTER; +} + +DWORD +NetServer::Listener() +{ + while (!server_shutdown) { + NetSock* s = sock.accept(&client_addr); + + while (s) { + sync.acquire(); + + for (int i = 0; i < poolsize; i++) { + if (conn[i] == 0) { + conn[i] = s; + clients[i] = client_addr; + s = 0; + break; + } + } + + sync.release(); + + // wait for a thread to become not busy + if (s) + Sleep(10); + } + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +DWORD WINAPI NetServerReaderProc(LPVOID link) +{ + if (!link) return (DWORD) E_POINTER; + + PoolItem* item = (PoolItem*) link; + NetServer* net_server = item->server; + int index = item->thread_index; + + delete item; + + if (net_server) + return net_server->Reader(index); + + return (DWORD) E_POINTER; +} + +DWORD +NetServer::Reader(int index) +{ + // init random seed for this thread: + srand(timeGetTime()); + + while (!server_shutdown) { + sync.acquire(); + NetSock* s = conn[index]; + sync.release(); + + if (s) { + const int MAX_REQUEST = 4096; + Text request; + + /*** + *** NOT SURE WHY, BUT THIS DOESN'T WORK FOR SHIT + *** + *** Setting the socket timeout to 2 seconds caused it + *** to wait for two seconds, read nothing, and give up + *** with a WSAETIMEDOUT error. Meanwhile, the client + *** immediately registered a failure (during the 2 sec + *** delay) and aborted the request. + *** + + s->set_timeout(2000); + Text msg = s->recv(); + + while (msg.length() > 0 && request.length() < MAX_REQUEST) { + request += msg; + msg = s->recv(); + } + + ***/ + + request = s->recv(); + + if (request.length() > 0 && !s->is_closed()) { + Text response = ProcessRequest(request, clients[index]); + err = s->send(response); + if (err < 0) { + err = NetLayer::GetLastError(); + } + } + + sync.acquire(); + delete conn[index]; + conn[index] = 0; + sync.release(); + } + else { + Sleep(5); + } + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +Text +NetServer::ProcessRequest(Text msg, const NetAddr& addr) +{ + if (msg.indexOf("GET ") == 0) + return DefaultResponse(); + + return ErrorResponse(); +} + +Text +NetServer::DefaultResponse() +{ + Text response = + "HTTP/1.0 200 OK\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n"; + + response += "Generic NetServer 1.0\n\n"; + response += "\n"; + response += "

Generic NetServer 1.0

\n"; + response += "

Didn't think I could do it, did ya?\n"; + response += "\n\n"; + + return response; +} + +Text +NetServer::ErrorResponse() +{ + Text response = + "HTTP/1.0 501 Not Implemented\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n"; + + response += "Generic NetServer 1.0\n\n"; + response += "\n"; + response += "

Generic NetServer 1.0

\n"; + response += "

Sorry charlie... I'm not a magician.\n"; + response += "\n\n"; + + return response; +} diff --git a/NetEx/NetServer.h b/NetEx/NetServer.h index d8996d7..f0b261f 100644 --- a/NetEx/NetServer.h +++ b/NetEx/NetServer.h @@ -1,65 +1,89 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetServer.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network Server Pump for HTTP Server -*/ - - -#ifndef NetServer_h -#define NetServer_h - -#include -#include "NetAddr.h" -#include "NetGram.h" -#include "NetSock.h" -#include "List.h" - -// +-------------------------------------------------------------------+ - -class NetServer -{ -public: - static const char* TYPENAME() { return "NetServer"; } - - NetServer(WORD port, int poolsize=1); - virtual ~NetServer(); - - int operator == (const NetServer& l) const { return addr == l.addr; } - - virtual void Shutdown(); - virtual DWORD Listener(); - virtual DWORD Reader(int index); - - virtual Text ProcessRequest(Text request, const NetAddr& addr); - virtual Text DefaultResponse(); - virtual Text ErrorResponse(); - - const NetAddr& GetAddress() const { return addr; } - int GetLastError() const { return err; } - -protected: - NetAddr addr; - NetSock sock; - NetAddr client_addr; - - int poolsize; - HANDLE hreader; - HANDLE* pool; - NetSock** conn; - NetAddr* clients; - int err; - bool server_shutdown; - - ThreadSync sync; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetServer.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network Server Pump for HTTP Server +*/ + + +#ifndef NetServer_h +#define NetServer_h + +#include +#include "NetAddr.h" +#include "NetGram.h" +#include "NetSock.h" +#include "List.h" + +// +-------------------------------------------------------------------+ + +class NetServer +{ +public: + static const char* TYPENAME() { return "NetServer"; } + + NetServer(WORD port, int poolsize=1); + virtual ~NetServer(); + + int operator == (const NetServer& l) const { return addr == l.addr; } + + virtual void Shutdown(); + virtual DWORD Listener(); + virtual DWORD Reader(int index); + + virtual Text ProcessRequest(Text request, const NetAddr& addr); + virtual Text DefaultResponse(); + virtual Text ErrorResponse(); + + const NetAddr& GetAddress() const { return addr; } + int GetLastError() const { return err; } + +protected: + NetAddr addr; + NetSock sock; + NetAddr client_addr; + + int poolsize; + HANDLE hreader; + HANDLE* pool; + NetSock** conn; + NetAddr* clients; + int err; + bool server_shutdown; + + ThreadSync sync; +}; + + #endif NetServer_h \ No newline at end of file diff --git a/NetEx/NetSock.cpp b/NetEx/NetSock.cpp index 47c50d8..0fb1081 100644 --- a/NetEx/NetSock.cpp +++ b/NetEx/NetSock.cpp @@ -1,342 +1,366 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetSock.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network (IP) Socket Wrapper Implementation -*/ - - -// WINSOCK2.H MUST COME FIRST!! -#include - -#include "MemDebug.h" -#include "NetSock.h" -#include "NetLayer.h" - -// +-------------------------------------------------------------------+ - -/** - * Server-side socket constructor - */ -NetSock::NetSock(bool str) - : stream(str), closed(false), stat(0), current_timeout(9999) -{ - if (stream) - s = ::socket(AF_INET, SOCK_STREAM, 0); - else - s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); -} - -/** - * Server-side socket constructor - * - * PRIVATE: used only by the accept call to build a socket for - * a client connection to this server - */ -NetSock::NetSock(SOCKET sock, bool str) - : s(sock), stream(str), closed(false), stat(0), current_timeout(9999) -{ } - -/** - * Client-side socket constructor - * - * Will connect to server at "addr" - */ -NetSock::NetSock(const NetAddr& addr, bool str) - : stream(str), closed(false), stat(0), current_timeout(9999) -{ - if (stream) - s = ::socket(AF_INET, SOCK_STREAM, 0); - else - s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - connect(addr); -} - -NetSock::~NetSock() -{ - close(); -} - -// +--------------------------------------------------------------------+ - -int -NetSock::bind(const NetAddr& addr) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - return stat = ::bind(s, addr.GetSockAddr(), addr.GetSockAddrLength()); -} - -int -NetSock::connect(const NetAddr& addr) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - return stat = ::connect(s, addr.GetSockAddr(), addr.GetSockAddrLength()); -} - -int -NetSock::listen(int max_connections) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - return stat = ::listen(s, max_connections); -} - -NetSock* -NetSock::accept(NetAddr* addr) -{ - if (closed || s == INVALID_SOCKET) return 0; - - SOCKET conn = INVALID_SOCKET; - - if (addr) { - sockaddr a; - int asize = sizeof(a); - - conn = ::accept(s, &a, &asize); - - if (conn != INVALID_SOCKET && asize > 0) { - addr->SetSockAddr(&a, asize); - } - } - else { - conn = ::accept(s, 0, 0); - } - - if (conn == INVALID_SOCKET) - return 0; - - return new(__FILE__,__LINE__) NetSock(conn, stream); -} - -// +--------------------------------------------------------------------+ - -int -NetSock::available() -{ - if (closed || s == INVALID_SOCKET) return 0; - - DWORD nbytes = 0; - if (::ioctlsocket(s, FIONREAD, &nbytes) == 0) - return (int) nbytes; - - return 0; -} - -// +--------------------------------------------------------------------+ - -int -NetSock::send(Text msg) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - return stat = ::send(s, msg.data(), msg.length(), 0); -} - -Text -NetSock::recv() -{ - if (closed || s == INVALID_SOCKET) return ""; - - static char rbuf[8192]; - int rlen = -1; - - if (stream) { - rlen = ::recv(s, rbuf, sizeof(rbuf), 0); - } - else { - rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0); - } - - if (rlen < 0) { - stat = NetLayer::GetLastError(); - - switch (stat) { - case WSAENETDOWN: - case WSAENETRESET: - case WSAEINTR: - case WSAESHUTDOWN: - case WSAECONNABORTED: - case WSAECONNRESET: - case WSAETIMEDOUT: - close(); - break; - - case WSAEWOULDBLOCK: - stat = WSAEWOULDBLOCK; - break; - } - - return Text(); - } - - else if (rlen == 0) { - return Text(); - } - - return Text(rbuf, rlen); -} - - -// +--------------------------------------------------------------------+ - -int -NetSock::sendto(Text msg, const NetAddr& dest) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - return stat = ::sendto(s, msg.data(), msg.length(), - 0, dest.GetSockAddr(), dest.GetSockAddrLength()); -} - -Text -NetSock::recvfrom(NetAddr* a) -{ - if (closed || s == INVALID_SOCKET) return ""; - - static char rbuf[4096]; - int rlen = 0; - - if (a) { - int addrlen = a->GetSockAddrLength(); - rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, a->GetSockAddr(), &addrlen); - a->InitFromSockAddr(); - } - else { - rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0); - } - - if (rlen < 0) { - stat = NetLayer::GetLastError(); - return Text(); - } - - else if (rlen == 0) { - return Text(); - } - - return Text(rbuf, rlen); -} - -// +--------------------------------------------------------------------+ - -int -NetSock::select(SELECT_TYPE t, long seconds, long microseconds) -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - - FD_SET fd; - ZeroMemory(&fd, sizeof(fd)); - - FD_SET(s, &fd); - TIMEVAL timeval = {seconds, microseconds}; - TIMEVAL* timeout = &timeval; - - if (t == SELECT_READ) - return stat = ::select(1, &fd, 0, 0, timeout); - - else if (t == SELECT_WRITE) - return stat = ::select(1, 0, &fd, 0, timeout); - - else if (t == (SELECT_READ|SELECT_WRITE)) - return stat = ::select(1, &fd, &fd, 0, timeout); - - return 0; -} - -// +--------------------------------------------------------------------+ - -int -NetSock::set_timeout(int msecs) -{ - if (closed || s == INVALID_SOCKET) return 0; - if (msecs == current_timeout) return 1; - - // zero timeout means non-blocking - if (msecs == 0) { - u_long nonblocking = 1; - if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) { - stat = NetLayer::GetLastError(); - return stat; - } - } - - // non-zero timeout means blocking - else { - if (current_timeout == 0) { - u_long nonblocking = 0; // disable non-blocking - if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) { - stat = NetLayer::GetLastError(); - return stat; - } - } - - // max timeout means infinite wait - if (msecs >= NET_MAX_TIMEOUT) { - int maxto = 0; - ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &maxto, sizeof(maxto)); - ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &maxto, sizeof(maxto)); - } - - // otherwise, set the timeout - else { - ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &msecs, sizeof(msecs)); - ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &msecs, sizeof(msecs)); - } - } - - current_timeout = msecs; - return 1; -} - -// +--------------------------------------------------------------------+ - -int -NetSock::shutdown_input() -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - ::shutdown(s, SD_RECEIVE); - return 0; -} - -// +--------------------------------------------------------------------+ - -int -NetSock::shutdown_output() -{ - if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; - ::shutdown(s, SD_SEND); - return 0; -} - -// +--------------------------------------------------------------------+ - -int -NetSock::close() -{ - if (s != INVALID_SOCKET && !closed) { - ::shutdown(s, SD_BOTH); - ::closesocket(s); - - closed = true; - } - - return 0; -} - -// +--------------------------------------------------------------------+ - -DWORD -NetSock::max_packet_size() const -{ - DWORD size = 0; - int len = sizeof(size); - - ::getsockopt(s, SOL_SOCKET, 0x2003, (char*) &size, &len); - - return size; -} \ No newline at end of file +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetSock.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network (IP) Socket Wrapper Implementation +*/ + + +// WINSOCK2.H MUST COME FIRST!! +#include + +#include "MemDebug.h" +#include "NetSock.h" +#include "NetLayer.h" + +// +-------------------------------------------------------------------+ + +/** + * Server-side socket constructor + */ +NetSock::NetSock(bool str) + : stream(str), closed(false), stat(0), current_timeout(9999) +{ + if (stream) + s = ::socket(AF_INET, SOCK_STREAM, 0); + else + s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +} + +/** + * Server-side socket constructor + * + * PRIVATE: used only by the accept call to build a socket for + * a client connection to this server + */ +NetSock::NetSock(SOCKET sock, bool str) + : s(sock), stream(str), closed(false), stat(0), current_timeout(9999) +{ } + +/** + * Client-side socket constructor + * + * Will connect to server at "addr" + */ +NetSock::NetSock(const NetAddr& addr, bool str) + : stream(str), closed(false), stat(0), current_timeout(9999) +{ + if (stream) + s = ::socket(AF_INET, SOCK_STREAM, 0); + else + s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + connect(addr); +} + +NetSock::~NetSock() +{ + close(); +} + +// +--------------------------------------------------------------------+ + +int +NetSock::bind(const NetAddr& addr) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + return stat = ::bind(s, addr.GetSockAddr(), addr.GetSockAddrLength()); +} + +int +NetSock::connect(const NetAddr& addr) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + return stat = ::connect(s, addr.GetSockAddr(), addr.GetSockAddrLength()); +} + +int +NetSock::listen(int max_connections) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + return stat = ::listen(s, max_connections); +} + +NetSock* +NetSock::accept(NetAddr* addr) +{ + if (closed || s == INVALID_SOCKET) return 0; + + SOCKET conn = INVALID_SOCKET; + + if (addr) { + sockaddr a; + int asize = sizeof(a); + + conn = ::accept(s, &a, &asize); + + if (conn != INVALID_SOCKET && asize > 0) { + addr->SetSockAddr(&a, asize); + } + } + else { + conn = ::accept(s, 0, 0); + } + + if (conn == INVALID_SOCKET) + return 0; + + return new(__FILE__,__LINE__) NetSock(conn, stream); +} + +// +--------------------------------------------------------------------+ + +int +NetSock::available() +{ + if (closed || s == INVALID_SOCKET) return 0; + + DWORD nbytes = 0; + if (::ioctlsocket(s, FIONREAD, &nbytes) == 0) + return (int) nbytes; + + return 0; +} + +// +--------------------------------------------------------------------+ + +int +NetSock::send(Text msg) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + return stat = ::send(s, msg.data(), msg.length(), 0); +} + +Text +NetSock::recv() +{ + if (closed || s == INVALID_SOCKET) return ""; + + static char rbuf[8192]; + int rlen = -1; + + if (stream) { + rlen = ::recv(s, rbuf, sizeof(rbuf), 0); + } + else { + rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0); + } + + if (rlen < 0) { + stat = NetLayer::GetLastError(); + + switch (stat) { + case WSAENETDOWN: + case WSAENETRESET: + case WSAEINTR: + case WSAESHUTDOWN: + case WSAECONNABORTED: + case WSAECONNRESET: + case WSAETIMEDOUT: + close(); + break; + + case WSAEWOULDBLOCK: + stat = WSAEWOULDBLOCK; + break; + } + + return Text(); + } + + else if (rlen == 0) { + return Text(); + } + + return Text(rbuf, rlen); +} + + +// +--------------------------------------------------------------------+ + +int +NetSock::sendto(Text msg, const NetAddr& dest) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + return stat = ::sendto(s, msg.data(), msg.length(), + 0, dest.GetSockAddr(), dest.GetSockAddrLength()); +} + +Text +NetSock::recvfrom(NetAddr* a) +{ + if (closed || s == INVALID_SOCKET) return ""; + + static char rbuf[4096]; + int rlen = 0; + + if (a) { + int addrlen = a->GetSockAddrLength(); + rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, a->GetSockAddr(), &addrlen); + a->InitFromSockAddr(); + } + else { + rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0); + } + + if (rlen < 0) { + stat = NetLayer::GetLastError(); + return Text(); + } + + else if (rlen == 0) { + return Text(); + } + + return Text(rbuf, rlen); +} + +// +--------------------------------------------------------------------+ + +int +NetSock::select(SELECT_TYPE t, long seconds, long microseconds) +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + + FD_SET fd; + ZeroMemory(&fd, sizeof(fd)); + + FD_SET(s, &fd); + TIMEVAL timeval = {seconds, microseconds}; + TIMEVAL* timeout = &timeval; + + if (t == SELECT_READ) + return stat = ::select(1, &fd, 0, 0, timeout); + + else if (t == SELECT_WRITE) + return stat = ::select(1, 0, &fd, 0, timeout); + + else if (t == (SELECT_READ|SELECT_WRITE)) + return stat = ::select(1, &fd, &fd, 0, timeout); + + return 0; +} + +// +--------------------------------------------------------------------+ + +int +NetSock::set_timeout(int msecs) +{ + if (closed || s == INVALID_SOCKET) return 0; + if (msecs == current_timeout) return 1; + + // zero timeout means non-blocking + if (msecs == 0) { + u_long nonblocking = 1; + if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) { + stat = NetLayer::GetLastError(); + return stat; + } + } + + // non-zero timeout means blocking + else { + if (current_timeout == 0) { + u_long nonblocking = 0; // disable non-blocking + if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) { + stat = NetLayer::GetLastError(); + return stat; + } + } + + // max timeout means infinite wait + if (msecs >= NET_MAX_TIMEOUT) { + int maxto = 0; + ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &maxto, sizeof(maxto)); + ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &maxto, sizeof(maxto)); + } + + // otherwise, set the timeout + else { + ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &msecs, sizeof(msecs)); + ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &msecs, sizeof(msecs)); + } + } + + current_timeout = msecs; + return 1; +} + +// +--------------------------------------------------------------------+ + +int +NetSock::shutdown_input() +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + ::shutdown(s, SD_RECEIVE); + return 0; +} + +// +--------------------------------------------------------------------+ + +int +NetSock::shutdown_output() +{ + if (closed || s == INVALID_SOCKET) return INVALID_SOCKET; + ::shutdown(s, SD_SEND); + return 0; +} + +// +--------------------------------------------------------------------+ + +int +NetSock::close() +{ + if (s != INVALID_SOCKET && !closed) { + ::shutdown(s, SD_BOTH); + ::closesocket(s); + + closed = true; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +DWORD +NetSock::max_packet_size() const +{ + DWORD size = 0; + int len = sizeof(size); + + ::getsockopt(s, SOL_SOCKET, 0x2003, (char*) &size, &len); + + return size; +} diff --git a/NetEx/NetSock.h b/NetEx/NetSock.h index 826d80c..00444f0 100644 --- a/NetEx/NetSock.h +++ b/NetEx/NetSock.h @@ -1,73 +1,97 @@ -/* Project nGenEx - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: NetEx.lib - FILE: NetSock.h - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Network (IP) Socket -*/ - -#ifndef NetSock_h -#define NetSock_h - -#include -#include "NetAddr.h" -#include "Text.h" - -// +-------------------------------------------------------------------+ - -#define NET_MAX_TIMEOUT 1e9 - -class NetSock -{ -public: - static const char* TYPENAME() { return "NetSock"; } - - enum SELECT_TYPE { - SELECT_READ = 1, - SELECT_WRITE = 2 - }; - - NetSock(bool stream=false); - NetSock(const NetAddr& addr, bool stream=false); - ~NetSock(); - - int bind(const NetAddr& addr); - int connect(const NetAddr& addr); - int listen(int max_connections=5); - NetSock* accept(NetAddr* addr=0); - int available(); - int send(Text msg); - Text recv(); - int sendto(Text msg, const NetAddr& dest); - Text recvfrom(NetAddr* a=0); - int select(SELECT_TYPE t=SELECT_READ, - long seconds=0, long microseconds=0); - - int shutdown_input(); - int shutdown_output(); - int set_timeout(int msecs); - int close(); - - DWORD max_packet_size() const; - bool is_stream() const { return stream; } - bool is_closed() const { return closed; } - int status() const { return stat; } - -private: - NetSock(SOCKET s, bool stream); - - SOCKET s; - bool stream; - bool closed; - int stat; - int current_timeout; -}; - - +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: NetEx.lib + FILE: NetSock.h + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Network (IP) Socket +*/ + +#ifndef NetSock_h +#define NetSock_h + +#include +#include "NetAddr.h" +#include "Text.h" + +// +-------------------------------------------------------------------+ + +#define NET_MAX_TIMEOUT 1e9 + +class NetSock +{ +public: + static const char* TYPENAME() { return "NetSock"; } + + enum SELECT_TYPE { + SELECT_READ = 1, + SELECT_WRITE = 2 + }; + + NetSock(bool stream=false); + NetSock(const NetAddr& addr, bool stream=false); + ~NetSock(); + + int bind(const NetAddr& addr); + int connect(const NetAddr& addr); + int listen(int max_connections=5); + NetSock* accept(NetAddr* addr=0); + int available(); + int send(Text msg); + Text recv(); + int sendto(Text msg, const NetAddr& dest); + Text recvfrom(NetAddr* a=0); + int select(SELECT_TYPE t=SELECT_READ, + long seconds=0, long microseconds=0); + + int shutdown_input(); + int shutdown_output(); + int set_timeout(int msecs); + int close(); + + DWORD max_packet_size() const; + bool is_stream() const { return stream; } + bool is_closed() const { return closed; } + int status() const { return stat; } + +private: + NetSock(SOCKET s, bool stream); + + SOCKET s; + bool stream; + bool closed; + int stat; + int current_timeout; +}; + + #endif NetSock_h \ No newline at end of file -- cgit v1.1