搜狗代理服务器程序,网上已经有
用 Python 实现的版本。这个版本在某些情况下(例如迅雷下载)性能不够好,于是我用 C++ 实现了一个版本,基于
POCO 框架开发,应当具有不错的可移植性。完整的源代码如下:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <vector> #include <string> #include <Poco/ErrorHandler.h> #include <Poco/Net/Socket.h> #include <Poco/Net/HTTPServer.h> #include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPServerRequestImpl.h> #include <Poco/Net/HTTPServerResponse.h> #include <Poco/Net/HTTPRequestHandler.h> #include <Poco/Net/HTTPClientSession.h> #include <Windows.h> using namespace std; using namespace Poco; using namespace Poco::Net; class EH : public ErrorHandler { public: virtual void exception(const Exception& exc) { puts(exc.message().c_str()); fflush(stdout); } virtual void exception(const std::exception& exc) { puts(exc.what()); fflush(stdout); } virtual void exception() { puts("Unknown exception"); fflush(stdout); } }; const int BUFFER_SIZE = 65536; char addressFormat[128]; int lower, upper; class ProxyService : public HTTPRequestHandler { HTTPClientSession client; static unsigned int hashTag(const string &s) { unsigned int code = s.length(); for (int i = 0; i < s.length() / 4; ++i) { unsigned int a = (s[i * 4] & 0xffu) + ((s[i * 4 + 1] & 0xffu) << 8); unsigned int b = (s[i * 4 + 2] & 0xffu) + ((s[i * 4 + 3] & 0xffu) << 8); code += a; code ^= ((code << 5) ^ b) << 0xb; code += code >> 0xb; } switch (s.length() % 4) { case 1: code += s[s.length() - 1] & 0xffu; code ^= code << 0xa; code += code >> 1; break; case 2: code += (s[s.length() - 2] & 0xffu) + ((s[s.length() - 1] & 0xffu) << 8); code ^= code << 0xb; code += code >> 0x11; break; case 3: code += (s[s.length() - 3] & 0xffu) + ((s[s.length() - 2] & 0xffu) << 8); code ^= (code ^ ((s[s.length() - 1] & 0xffu) << 2)) << 0x10; code += code >> 0xb; break; } code ^= code << 3; code += code >> 5; code ^= code << 4; code += code >> 0x11; code ^= code << 0x19; code += code >> 6; return code; } static string hexString(unsigned int x) { char buff[128]; sprintf(buff, "%08x", x); return string(buff, buff + 8); } void write(istream &in, ostream &out) { char buffer[BUFFER_SIZE]; streamsize sz; while (in.good() && out.good()) { in.read(buffer, sizeof(buffer)); sz = in.gcount(); if (sz <= 0) break; if (out.good()) out.write(buffer, sz); } } public: ProxyService() { static int xid; char host[128]; int id = xid + 1; if (id > upper || id < lower) id = lower; xid = id; sprintf(host, addressFormat, id); client.setHost(host); client.setPort(80); } virtual void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) { printf("[%s] %s\n", request.getMethod().c_str(), request.getURI().c_str()); fflush(stdout); static const string authToken = "5FA7CF6A1809B3B514FA66A1ECA16FEB/30/78e7ff933a9a3063"; string timestamp(hexString((unsigned)time(0))); string tag(hexString(hashTag(timestamp + request.getHost() + "SogouExplorerProxy"))); try { HTTPRequest clientRequest(request.getMethod(), request.getURI(), request.getVersion()); for (NameValueCollection::ConstIterator i = request.begin(); i != request.end(); ++i) clientRequest.set(i->first, i->second); clientRequest.set("X-Sogou-Auth", authToken); clientRequest.set("X-Sogou-Tag", tag); clientRequest.set("X-Sogou-Timestamp", timestamp); ostream& os = client.sendRequest(clientRequest); os.flush(); if (toUpper(request.getMethod()) == "POST") { write(request.stream(), os); os.flush(); } HTTPResponse clientResponse; istream& is = client.receiveResponse(clientResponse); response.setStatusAndReason(clientResponse.getStatus(), clientResponse.getReason()); for (NameValueCollection::ConstIterator i = clientResponse.begin(); i != clientResponse.end(); ++i) response.set(i->first, i->second); ostream& out = response.send(); out.flush(); if (toUpper(request.getMethod()) == "CONNECT" && clientResponse.getStatus() == HTTPResponse::HTTP_OK) { char buffer[BUFFER_SIZE]; int sz; istream &in = request.stream(); sz = (int)is.readsome(buffer, sizeof(buffer)); if (sz > 0) { out.write(buffer, sz); out.flush(); } sz = (int)in.readsome(buffer, sizeof(buffer)); if (sz > 0) { os.write(buffer, sz); os.flush(); } StreamSocket s1 = client.socket(); StreamSocket s2 = ((HTTPServerRequestImpl*)&request)->socket(); vector<Socket> list, l2, l3; while (true) { list.clear(); list.push_back(s1); list.push_back(s2); Socket::select(list, l2, l3, Timespan(20, 0)); if (list.empty()) break; if (list[0] == s2) { sz = s2.receiveBytes(buffer, sizeof(buffer)); if (sz <= 0) break; s1.sendBytes(buffer, sz); } else { sz = s1.receiveBytes(buffer, sizeof(buffer)); if (sz <= 0) break; s2.sendBytes(buffer, sz); } } } else { write(is, out); out.flush(); } } catch (const Exception &) { client.reset(); } } }; class ProxyRequestHandlerFactory : public HTTPRequestHandlerFactory { public: virtual HTTPRequestHandler *createRequestHandler(const HTTPServerRequest & request) { return new ProxyService; } }; int main(int argc, char **argv) { FILE *fp = fopen("config.txt", "r"); if (!fp) { puts("Fail to open config.txt"); return 1; } if (fscanf(fp, "%d%d%s", &lower, &upper, addressFormat) != 3) { puts("Invalid config.txt"); return 2; } fclose(fp); ThreadPool::defaultPool().addCapacity(256 - ThreadPool::defaultPool().capacity()); ErrorHandler::set(new EH); ServerSocket socket; socket.bind(SocketAddress(8118), true); socket.listen(); HTTPServerParams *params = new HTTPServerParams; params->setMaxThreads(128); params->setServerName("ProxyService"); params->setSoftwareVersion("1.0"); HTTPServer server(new ProxyRequestHandlerFactory(), socket, params); server.start(); while (true) { Thread::sleep(65535); } server.stop(); return 0; }