示例#1
0
int main(int, char**)
{
    HttpServer server;
    server.start();
    server.setSink(&handler);
    server.bind("localhost", {"server.crt", "server.key", "", "", "", ""}, "8080");
    while (true)
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
示例#2
0
void CP2PServiceModule::OnCheckTimer( UINT uID, UINT uTime )
{
	if ( !g_socketClientMgr.IsConnected())
	{
		CKLog::WriteLog( LOG_TYPE_DEBUG, "reconnect server.%d", GetTickCount());
		g_server.stop();
		g_socketClientMgr.Release();
		if ( g_socketClientMgr.Connect())
			g_server.start();
	}
}
int main() {
  // HTTP-server at port 8080 using 1 thread
  // Unless you do more heavy non-threaded processing in the resources,
  // 1 thread is usually faster than several threads
  HttpServer server;
  server.config.port = 8080;

  // Add resources using path-regex and method-string, and an anonymous function
  // POST-example for the path /string, responds the posted string
  server.resource["^/string$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    // Retrieve string:
    auto content = request->content.string();
    // request->content.string() is a convenience function for:
    // stringstream ss;
    // ss << request->content.rdbuf();
    // auto content=ss.str();

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n"
              << content;


    // Alternatively, use one of the convenience functions, for instance:
    // response->write(content);
  };

  // POST-example for the path /json, responds firstName+" "+lastName from the posted json
  // Responds with an appropriate error message if the posted json is not valid, or if firstName or lastName is missing
  // Example posted json:
  // {
  //   "firstName": "John",
  //   "lastName": "Smith",
  //   "age": 25
  // }
  server.resource["^/json$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    try {
      ptree pt;
      read_json(request->content, pt);

      auto name = pt.get<string>("firstName") + " " + pt.get<string>("lastName");

      *response << "HTTP/1.1 200 OK\r\n"
                << "Content-Length: " << name.length() << "\r\n\r\n"
                << name;
    }
    catch(const exception &e) {
      *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n"
                << e.what();
    }


    // Alternatively, using a convenience function:
    // try {
    //     ptree pt;
    //     read_json(request->content, pt);

    //     auto name=pt.get<string>("firstName")+" "+pt.get<string>("lastName");
    //     response->write(name);
    // }
    // catch(const exception &e) {
    //     response->write(SimpleWeb::StatusCode::client_error_bad_request, e.what());
    // }
  };

  // GET-example for the path /info
  // Responds with request-information
  server.resource["^/info$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    stringstream stream;
    stream << "<h1>Request from " << request->remote_endpoint_address() << ":" << request->remote_endpoint_port() << "</h1>";

    stream << request->method << " " << request->path << " HTTP/" << request->http_version;

    stream << "<h2>Query Fields</h2>";
    auto query_fields = request->parse_query_string();
    for(auto &field : query_fields)
      stream << field.first << ": " << field.second << "<br>";

    stream << "<h2>Header Fields</h2>";
    for(auto &field : request->header)
      stream << field.first << ": " << field.second << "<br>";

    response->write(stream);
  };

  // GET-example for the path /match/[number], responds with the matched string in path (number)
  // For instance a request GET /match/123 will receive: 123
  server.resource["^/match/([0-9]+)$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    response->write(request->path_match[1]);
  };

  // GET-example simulating heavy work in a separate thread
  server.resource["^/work$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
    thread work_thread([response] {
      this_thread::sleep_for(chrono::seconds(5));
      response->write("Work done");
    });
    work_thread.detach();
  };

  // Default GET-example. If no other matches, this anonymous function will be called.
  // Will respond with content in the web/-directory, and its subdirectories.
  // Default file: index.html
  // Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server
  server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    try {
      auto web_root_path = boost::filesystem::canonical("web");
      auto path = boost::filesystem::canonical(web_root_path / request->path);
      // Check if path is within web_root_path
      if(distance(web_root_path.begin(), web_root_path.end()) > distance(path.begin(), path.end()) ||
         !equal(web_root_path.begin(), web_root_path.end(), path.begin()))
        throw invalid_argument("path must be within root path");
      if(boost::filesystem::is_directory(path))
        path /= "index.html";

      SimpleWeb::CaseInsensitiveMultimap header;

      // Uncomment the following line to enable Cache-Control
      // header.emplace("Cache-Control", "max-age=86400");

#ifdef HAVE_OPENSSL
//    Uncomment the following lines to enable ETag
//    {
//      ifstream ifs(path.string(), ifstream::in | ios::binary);
//      if(ifs) {
//        auto hash = SimpleWeb::Crypto::to_hex_string(SimpleWeb::Crypto::md5(ifs));
//        header.emplace("ETag", "\"" + hash + "\"");
//        auto it = request->header.find("If-None-Match");
//        if(it != request->header.end()) {
//          if(!it->second.empty() && it->second.compare(1, hash.size(), hash) == 0) {
//            response->write(SimpleWeb::StatusCode::redirection_not_modified, header);
//            return;
//          }
//        }
//      }
//      else
//        throw invalid_argument("could not read file");
//    }
#endif

      auto ifs = make_shared<ifstream>();
      ifs->open(path.string(), ifstream::in | ios::binary | ios::ate);

      if(*ifs) {
        auto length = ifs->tellg();
        ifs->seekg(0, ios::beg);

        header.emplace("Content-Length", to_string(length));
        response->write(header);

        // Trick to define a recursive function within this scope (for example purposes)
        class FileServer {
        public:
          static void read_and_send(const shared_ptr<HttpServer::Response> &response, const shared_ptr<ifstream> &ifs) {
            // Read and send 128 KB at a time
            static vector<char> buffer(131072); // Safe when server is running on one thread
            streamsize read_length;
            if((read_length = ifs->read(&buffer[0], static_cast<streamsize>(buffer.size())).gcount()) > 0) {
              response->write(&buffer[0], read_length);
              if(read_length == static_cast<streamsize>(buffer.size())) {
                response->send([response, ifs](const SimpleWeb::error_code &ec) {
                  if(!ec)
                    read_and_send(response, ifs);
                  else
                    cerr << "Connection interrupted" << endl;
                });
              }
            }
          }
        };
        FileServer::read_and_send(response, ifs);
      }
      else
        throw invalid_argument("could not read file");
    }
    catch(const exception &e) {
      response->write(SimpleWeb::StatusCode::client_error_bad_request, "Could not open path " + request->path + ": " + e.what());
    }
  };

  server.on_error = [](shared_ptr<HttpServer::Request> /*request*/, const SimpleWeb::error_code & /*ec*/) {
    // Handle errors here
    // Note that connection timeouts will also call this handle with ec set to SimpleWeb::errc::operation_canceled
  };

  thread server_thread([&server]() {
    // Start server
    server.start();
  });

  // Wait for server to start so that the client can connect
  this_thread::sleep_for(chrono::seconds(1));

  // Client examples
  HttpClient client("localhost:8080");

  string json_string = "{\"firstName\": \"John\",\"lastName\": \"Smith\",\"age\": 25}";

  // Synchronous request examples
  try {
    auto r1 = client.request("GET", "/match/123");
    cout << r1->content.rdbuf() << endl; // Alternatively, use the convenience function r1->content.string()

    auto r2 = client.request("POST", "/string", json_string);
    cout << r2->content.rdbuf() << endl;
  }
  catch(const SimpleWeb::system_error &e) {
    cerr << "Client request error: " << e.what() << endl;
  }

  // Asynchronous request example
  client.request("POST", "/json", json_string, [](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
    if(!ec)
      cout << response->content.rdbuf() << endl;
  });
  client.io_service->run();

  server_thread.join();
}
示例#4
0
bool test_http1_1_chunked_request() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    bool gotExpectedPost = false;
    HttpError postError = HttpError::Ok;
    bool hadCorrectHeader = false;

    std::tie(acceptSocket, socketErr) = ::getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/formthingy", [&gotExpectedPost, &postError, &hadCorrectHeader](HttpRequest& request, HttpResponse& response) {
        char readBuffer[1024];
        HttpInputStream& httpInputStream = request.getInputStream();

        HttpError readErr;
        uint32_t readBytes;
        uint32_t totalRead = 0;
        do {
            std::tie(readBytes, readErr) = httpInputStream.read(readBuffer + totalRead, 3); // Intentionally small reads
            totalRead += readBytes;
        } while (readErr == HttpError::Ok);

        if (readErr != HttpError::Eof) {
            postError = readErr;
            return;
        }

        postError = httpInputStream.close();

        const StringRef postData = "Some form post data";
        if (totalRead != postData.length()) {
            gotExpectedPost = false;
        } else {
            gotExpectedPost = std::memcmp(postData.data(), readBuffer, postData.length()) == 0;
        }

        StringRef transferEncodingHeader;
        bool hasHeader;
        std::tie(transferEncodingHeader, hasHeader) = request.getHeader("Transfer-Encoding");
        if (!hasHeader) {
            hadCorrectHeader = false;
        } else {
            hadCorrectHeader = transferEncodingHeader.equals("chunked");
        }

        response.setStatus(200, "OK");
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    StringRef request =
        "POST /formthingy HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Transfer-Encoding: chunked\r\n"
        "Connection: close\r\n"
        "\r\n"
        "5\r\n"
        "Some \r\n"
        "e\r\n"
        "form post data\r\n"
        "0\r\n"
        "\r\n";
    StringRef expectedResponse = "HTTP/1.1 200 OK\r\n\r\n";
    char responseBuffer[1024];

    Socket requestSocket;
    std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
    if (socketErr != SocketError::Ok) {
        testf("Failed to connect to HTTP socket with: %d", socketErr);
        return false;
    }
    socketErr = requestSocket.write(request.data(), request.length());
    if (socketErr != SocketError::Ok) {
        testf("Failed to write to HTTP socket with: %d", socketErr);
        return false;
    }
    uint32_t totalBytesRead = 0;
    std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
    if (socketErr != SocketError::Ok) {
        testf("Failed to read from HTTP socket with: %d", socketErr);
        return false;
    }
    if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
        testf("Did not receive expected response. Got:\n%.*s", totalBytesRead, responseBuffer);
        return false;
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }

    if (!gotExpectedPost) {
        testf("Did not read back expected post data");
        return false;
    }
    if (postError != HttpError::Ok) {
        testf("Received error when reading post data: %d", postError);
        return false;
    }
    if (!hadCorrectHeader) {
        testf("Http request did not have expected Transfer-Encoding header");
        return false;
    }

    return true;
}
示例#5
0
bool test_http1_1_keepalive() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/index.html", [](HttpRequest& request, HttpResponse& response) {
        response.setStatus(200, "OK");
        response.addHeader("Content-Length", "11");

        HttpOutputStream* outputStream;
        std::tie(outputStream, std::ignore) = response.getOutputStream();
        outputStream->write("Hello World", 11);
        outputStream->close();
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    std::vector<StringRef> requests = {
        "GET /index.html HTTP/1.1\r\n"
        "Connection: keep-alive\r\n"
        "Host: localhost\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n",

        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "\r\n",
    };
    std::vector<StringRef> expectedResponses = {
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World"
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World"
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World",

        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World",
    };

    for (size_t i = 0; i < requests.size(); i++) {
        StringRef request = requests[i];
        StringRef expectedResponse = expectedResponses[i];

        char responseBuffer[1024];

        Socket requestSocket;
        std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
        if (socketErr != SocketError::Ok) {
            testf("Failed to connect to HTTP socket with: %d", socketErr);
            return false;
        }
        socketErr = requestSocket.write(request.data(), request.length());
        if (socketErr != SocketError::Ok) {
            testf("Failed to write to HTTP socket with: %d", socketErr);
            return false;
        }
        uint32_t totalBytesRead = 0;
        std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
        if (socketErr != SocketError::Ok) {
            testf("Failed to read from HTTP socket with: %d", socketErr);
            return false;
        }
        if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
            testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer);
            return false;
        }
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }

    return true;
}
示例#6
0
// Tests that a chunked header is automatically added, and chunked mode is used,
// if neither Transfer-Encoding or Content-Length are specified.
bool test_http1_1_auto_chunked_response() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError = HttpError::Ok;
    HttpError responseError = HttpError::Ok;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/index.html", [&responseError](HttpRequest& request, HttpResponse& response) {
        response.setStatus(200, "OK");

        HttpOutputStream* outputStream;
        std::tie(outputStream, responseError) = response.getOutputStream();
        if (responseError != HttpError::Ok) {
            return;
        }
        responseError = outputStream->write("0123456789", 10);
        if (responseError != HttpError::Ok) {
            return;
        }
        responseError = outputStream->write("abc", 3);
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    StringRef request =
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n";
    StringRef expectedResponse =
        "HTTP/1.1 200 OK\r\n"
        "Transfer-Encoding: chunked\r\n"
        "\r\n"
        "A\r\n"
        "0123456789\r\n"
        "3\r\n"
        "abc\r\n"
        "0\r\n"
        "\r\n";
    char responseBuffer[1024];

    Socket requestSocket;
    std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
    if (socketErr != SocketError::Ok) {
        testf("Failed to connect to HTTP socket with: %d", socketErr);
        return false;
    }
    socketErr = requestSocket.write(request.data(), request.length());
    if (socketErr != SocketError::Ok) {
        testf("Failed to write to HTTP socket with: %d", socketErr);
        return false;
    }
    uint32_t totalBytesRead = 0;
    std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
    if (socketErr != SocketError::Ok) {
        testf("Failed to read from HTTP socket with: %d", socketErr);
        return false;
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }
    if (responseError != HttpError::Ok) {
        testf("Error when generating response: %d", responseError);
        return false;
    }
    if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
        testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer);
        return false;
    }

    return true;
}