mirror of
https://onedev.site.tesses.net/tesses-framework
synced 2026-02-08 07:45:46 +00:00
Add both Basic auth and CGI support
This commit is contained in:
@@ -16,6 +16,8 @@ src/Http/HttpStream.cpp
|
||||
src/Http/ContentDisposition.cpp
|
||||
src/Http/WebSocket.cpp
|
||||
src/Http/ChangeableServer.cpp
|
||||
src/Http/BasicAuthServer.cpp
|
||||
src/Http/CGIServer.cpp
|
||||
src/Mail/Smtp.cpp
|
||||
src/Serialization/Json.cpp
|
||||
src/Serialization/SQLite.cpp
|
||||
|
||||
@@ -9,7 +9,7 @@ using namespace Tesses::Framework::Threading;
|
||||
void print_help(const char* name)
|
||||
{
|
||||
printf("Tesses FileServer\nUSAGE: %s [OPTIONS] <dir>\n",name);
|
||||
printf("OPTIONS:\n-p PORT, --port PORT: Change port from 9852\n-l, --listing: Enable listing\n-s, --spa: Enable SPA mode (send \"/\" body instead of not found)\n-h, --help: This Screen\n");
|
||||
printf("OPTIONS:\n-p PORT, --port PORT: Change port from 9852\n-l, --listing: Enable listing\n-s, --spa: Enable SPA mode (send \"/\" body instead of not found)\n-c, --cgi-bin: Enable cgi (common gateway interface) support (specify a folder like /cgi-bin)\n-a, --cgi-admin: cgi admin email\n-w, --cgi-working: working directory for cgi scripts\n-h, --help: This Screen\n");
|
||||
exit(1);
|
||||
}
|
||||
int main(int argc, char** argv)
|
||||
@@ -21,6 +21,9 @@ int main(int argc, char** argv)
|
||||
const char* directory = "wwwroot";
|
||||
bool spa=false;
|
||||
bool allowListing = false;
|
||||
std::optional<std::string> cgi_bin;
|
||||
std::optional<std::string> cgi_admin;
|
||||
std::optional<Tesses::Framework::Filesystem::VFSPath> cgi_workdir;
|
||||
uint16_t port = 9852L;
|
||||
|
||||
for(int i = 1; i < argc; i++)
|
||||
@@ -29,6 +32,41 @@ int main(int argc, char** argv)
|
||||
{
|
||||
print_help(argv[0]);
|
||||
}
|
||||
else if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i],"--cgi-bin") == 0)
|
||||
{
|
||||
if(i+1>=argc)
|
||||
{
|
||||
printf("ERROR: Not enough arguments for cgi-bin\n");
|
||||
print_help(argv[0]);
|
||||
}
|
||||
else {
|
||||
printf("CGI is enabled\n");
|
||||
cgi_bin = argv[++i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-a") == 0 || strcmp(argv[i],"--cgi-admin") == 0)
|
||||
{
|
||||
if(i+1>=argc)
|
||||
{
|
||||
printf("ERROR: Not enough arguments for cgi-admin\n");
|
||||
print_help(argv[0]);
|
||||
}
|
||||
else {
|
||||
cgi_admin = argv[++i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-w") == 0 || strcmp(argv[i],"--cgi-working") == 0)
|
||||
{
|
||||
if(i+1>=argc)
|
||||
{
|
||||
printf("ERROR: Not enough arguments for cgi-working\n");
|
||||
print_help(argv[0]);
|
||||
}
|
||||
else {
|
||||
cgi_workdir = (Tesses::Framework::Filesystem::VFSPath)argv[++i];
|
||||
}
|
||||
}
|
||||
|
||||
else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--listing") == 0)
|
||||
{
|
||||
allowListing = true;
|
||||
@@ -57,10 +95,25 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
std::cout << "In folder: " << std::filesystem::absolute(directory).string() << std::endl;
|
||||
|
||||
|
||||
std::shared_ptr<IHttpServer> http;
|
||||
|
||||
auto fs = std::make_shared<FileServer>(directory,allowListing, spa);
|
||||
HttpServer server(port,fs);
|
||||
if(cgi_bin)
|
||||
{
|
||||
Tesses::Framework::Filesystem::VFSPath dir = *cgi_bin;
|
||||
dir.relative = true;
|
||||
auto svr = std::make_shared<MountableServer>(fs);
|
||||
auto cgi = std::make_shared<CGIServer>(std::filesystem::absolute(directory).string() / dir);
|
||||
cgi->adminEmail = cgi_admin;
|
||||
cgi->workingDirectory = cgi_workdir;
|
||||
svr->Mount(*cgi_bin, cgi);
|
||||
http = svr;
|
||||
}
|
||||
else {
|
||||
http = fs;
|
||||
}
|
||||
|
||||
HttpServer server(port,http);
|
||||
server.StartAccepting();
|
||||
TF_RunEventLoop();
|
||||
std::cout << "Closing server" << std::endl;
|
||||
|
||||
18
include/TessesFramework/Http/BasicAuthServer.hpp
Normal file
18
include/TessesFramework/Http/BasicAuthServer.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "HttpServer.hpp"
|
||||
namespace Tesses::Framework::Http {
|
||||
class BasicAuthServer : public Tesses::Framework::Http::IHttpServer
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<IHttpServer> server;
|
||||
std::function<bool(std::string username, std::string password)> authorization;
|
||||
std::string realm;
|
||||
|
||||
BasicAuthServer();
|
||||
BasicAuthServer(std::shared_ptr<IHttpServer> server, std::function<bool(std::string username, std::string password)> auth,std::string realm="Protected Content");
|
||||
bool Handle(ServerContext& ctx);
|
||||
|
||||
|
||||
static bool GetCreds(ServerContext& ctx, std::string& user, std::string& pass);
|
||||
};
|
||||
}
|
||||
23
include/TessesFramework/Http/CGIServer.hpp
Normal file
23
include/TessesFramework/Http/CGIServer.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "../Filesystem/VFS.hpp"
|
||||
#include "../Filesystem/VFSFix.hpp"
|
||||
#include "HttpServer.hpp"
|
||||
#include <optional>
|
||||
namespace Tesses::Framework::Http {
|
||||
struct CGIParams {
|
||||
std::optional<Tesses::Framework::Filesystem::VFSPath> document_root;
|
||||
Tesses::Framework::Filesystem::VFSPath program;
|
||||
std::optional<std::string> adminEmail;
|
||||
std::optional<Tesses::Framework::Filesystem::VFSPath> workingDirectory;
|
||||
};
|
||||
class CGIServer : public Tesses::Framework::Http::IHttpServer {
|
||||
Tesses::Framework::Filesystem::VFSPath dir;
|
||||
public:
|
||||
std::optional<Tesses::Framework::Filesystem::VFSPath> document_root;
|
||||
std::optional<std::string> adminEmail;
|
||||
std::optional<Tesses::Framework::Filesystem::VFSPath> workingDirectory;
|
||||
CGIServer(Tesses::Framework::Filesystem::VFSPath dir);
|
||||
bool Handle(ServerContext& ctx);
|
||||
static bool ServeCGIRequest(ServerContext& ctx, CGIParams& params);
|
||||
};
|
||||
}
|
||||
@@ -29,6 +29,7 @@ namespace Tesses::Framework::Http
|
||||
StatusCode statusCode;
|
||||
std::string ip;
|
||||
uint16_t port;
|
||||
uint16_t serverPort;
|
||||
std::string version;
|
||||
bool encrypted;
|
||||
ServerContext(std::shared_ptr<Tesses::Framework::Streams::Stream> strm);
|
||||
@@ -99,7 +100,7 @@ namespace Tesses::Framework::Http
|
||||
HttpServer(std::string unixPath, std::shared_ptr<IHttpServer> http);
|
||||
uint16_t GetPort();
|
||||
void StartAccepting();
|
||||
static void Process(std::shared_ptr<Tesses::Framework::Streams::Stream> strm, std::shared_ptr<IHttpServer> server, std::string ip, uint16_t port, bool encrypted);
|
||||
static void Process(std::shared_ptr<Tesses::Framework::Streams::Stream> strm, std::shared_ptr<IHttpServer> server, std::string ip, uint16_t port,uint16_t serverPort, bool encrypted);
|
||||
~HttpServer();
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "Http/MountableServer.hpp"
|
||||
#include "Http/ContentDisposition.hpp"
|
||||
#include "Http/ChangeableServer.hpp"
|
||||
#include "Http/CGIServer.hpp"
|
||||
#include "Http/BasicAuthServer.hpp"
|
||||
#include "Streams/FileStream.hpp"
|
||||
#include "Streams/MemoryStream.hpp"
|
||||
#include "Streams/NetworkStream.hpp"
|
||||
|
||||
47
src/Http/BasicAuthServer.cpp
Normal file
47
src/Http/BasicAuthServer.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "TessesFramework/Http/BasicAuthServer.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
namespace Tesses::Framework::Http {
|
||||
|
||||
BasicAuthServer::BasicAuthServer()
|
||||
{
|
||||
|
||||
}
|
||||
BasicAuthServer::BasicAuthServer(std::shared_ptr<IHttpServer> server, std::function<bool(std::string username, std::string password)> auth,std::string realm) : server(server), authorization(auth), realm(realm)
|
||||
{
|
||||
|
||||
}
|
||||
bool BasicAuthServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
std::string www_authenticate = "Basic realm=\"" + this->realm + "\", charset=\"UTF-8\"";
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if(!GetCreds(ctx,user,pass) || !this->authorization(user,pass)) {
|
||||
ctx.responseHeaders.SetValue("WWW-Authenticate",www_authenticate);
|
||||
ctx.statusCode = Unauthorized;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this->server)
|
||||
return this->server->Handle(ctx);
|
||||
ctx.statusCode = InternalServerError;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool BasicAuthServer::GetCreds(ServerContext& ctx, std::string& user, std::string& pass)
|
||||
{
|
||||
std::string auth;
|
||||
if(!ctx.requestHeaders.TryGetFirst("Authorization", auth)) return false;
|
||||
|
||||
auto security = HttpUtils::SplitString(auth," ",2);
|
||||
if(security.size() < 2) return false;
|
||||
if(security[0] != "Basic") return false;
|
||||
auto decoded = Crypto::Base64_Decode(security[0]);
|
||||
std::string decoded_str(decoded.begin(),decoded.end());
|
||||
security = HttpUtils::SplitString(auth,":",2);
|
||||
if(security.size() < 2) return false;
|
||||
user = security[0];
|
||||
pass = security[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
135
src/Http/CGIServer.cpp
Normal file
135
src/Http/CGIServer.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "TessesFramework/Http/CGIServer.hpp"
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include "TessesFramework/Platform/Process.hpp"
|
||||
#include "TessesFramework/Http/BasicAuthServer.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamReader.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Http {
|
||||
CGIServer::CGIServer(Tesses::Framework::Filesystem::VFSPath dir)
|
||||
{
|
||||
this->dir = dir;
|
||||
}
|
||||
bool CGIServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
Tesses::Framework::Filesystem::VFSPath execPath = ctx.path;
|
||||
execPath.relative=true;
|
||||
CGIParams params;
|
||||
params.document_root = this->document_root ? *this->document_root : this->dir;
|
||||
params.adminEmail = this->adminEmail;
|
||||
params.workingDirectory = this->workingDirectory;
|
||||
params.program = this->dir / execPath.CollapseRelativeParents();
|
||||
|
||||
return ServeCGIRequest(ctx,params);
|
||||
}
|
||||
bool CGIServer::ServeCGIRequest(ServerContext& ctx, CGIParams& params)
|
||||
{
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
auto program = params.program.MakeAbsolute();
|
||||
if(!LocalFS->FileExists(program)) return false;
|
||||
Tesses::Framework::Platform::Process p;
|
||||
|
||||
Tesses::Framework::Filesystem::VFSPath p0=ctx.originalPath;
|
||||
|
||||
p.env.emplace_back("SCRIPT_FILENAME",LocalFS->VFSPathToSystem(program));
|
||||
p.env.emplace_back("SCRIPT_NAME",p0.CollapseRelativeParents().ToString());
|
||||
if(ctx.encrypted)
|
||||
p.env.emplace_back("HTTPS","on");
|
||||
|
||||
std::string query;
|
||||
for(auto& item : ctx.queryParams.kvp)
|
||||
{
|
||||
for(auto& val : item.second)
|
||||
{
|
||||
if(!query.empty()) query += "&";
|
||||
|
||||
query += HttpUtils::UrlEncode(item.first);
|
||||
query += "=";
|
||||
query += HttpUtils::UrlEncode(val);
|
||||
}
|
||||
}
|
||||
p.env.emplace_back("QUERY_STRING",query);
|
||||
p.env.emplace_back("REQUEST_URI",ctx.GetOriginalPathWithQuery());
|
||||
p.env.emplace_back("REQUEST_METHOD",ctx.method);
|
||||
p.env.emplace_back("REMOTE_HOST",ctx.ip);
|
||||
p.env.emplace_back("REMOTE_ADDR",ctx.ip);
|
||||
p.env.emplace_back("REMOTE_PORT",std::to_string(ctx.port));
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if(BasicAuthServer::GetCreds(ctx,user,pass))
|
||||
p.env.emplace_back("REMOTE_USER",user);
|
||||
p.env.emplace_back("SERVER_SOFTWARE","TessesFrameworkWebServer");
|
||||
p.env.emplace_back("SERVER_PORT",std::to_string(ctx.serverPort));
|
||||
p.env.emplace_back("GATEWAY_INTERFACE","CGI/1.1");
|
||||
p.env.emplace_back("SERVER_PROTOCOL",ctx.version);
|
||||
|
||||
if(params.document_root)
|
||||
p.env.emplace_back("DOCUMENT_ROOT",params.document_root->ToString());
|
||||
if(params.adminEmail)
|
||||
p.env.emplace_back("SERVER_ADMIN",*params.adminEmail);
|
||||
|
||||
for(auto& hdr : ctx.requestHeaders.kvp)
|
||||
{
|
||||
std::string hdr_name = HttpUtils::ToUpper(hdr.first);
|
||||
if(hdr_name == "CONTENT-LENGTH")
|
||||
{
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH",hdr.second.front());
|
||||
}
|
||||
else if(hdr_name == "CONTENT-TYPE")
|
||||
{
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH",hdr.second.front());
|
||||
}
|
||||
else {
|
||||
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("HTTP_"+hdr.first,hdr.second.front());
|
||||
}
|
||||
}
|
||||
p.redirectStdIn=true;
|
||||
p.redirectStdOut=true;
|
||||
p.name = program.ToString();
|
||||
|
||||
|
||||
if(params.workingDirectory)
|
||||
{
|
||||
p.workingDirectory = params.workingDirectory->MakeAbsolute().ToString();
|
||||
}
|
||||
|
||||
if(p.Start())
|
||||
{
|
||||
auto strm = p.GetStdinStream();
|
||||
if(ctx.method != "GET") ctx.ReadStream(strm);
|
||||
p.CloseStdInNow();
|
||||
auto stout = p.GetStdoutStream();
|
||||
Tesses::Framework::TextStreams::StreamReader reader(stout);
|
||||
std::string line;
|
||||
while(reader.ReadLine(line))
|
||||
{
|
||||
auto v = HttpUtils::SplitString(line,": ", 2);
|
||||
if(v.size() == 2)
|
||||
{
|
||||
if(HttpUtils::ToLower(v[0]) == "status")
|
||||
{
|
||||
auto v2 = HttpUtils::SplitString(v[1]," ",2);
|
||||
if(v2.empty())
|
||||
{
|
||||
ctx.statusCode = StatusCode::InternalServerError;
|
||||
throw std::runtime_error("Status response is empty");
|
||||
}
|
||||
ctx.statusCode= (StatusCode)std::stoi(v2[0]);
|
||||
}
|
||||
else {
|
||||
ctx.responseHeaders.AddValue(v[0],v[1]);
|
||||
}
|
||||
}
|
||||
else throw std::runtime_error("Corrupted header: " + line);
|
||||
line.clear();
|
||||
}
|
||||
|
||||
ctx.SendStream(stout);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@ namespace Tesses::Framework::Http
|
||||
|
||||
bool FileServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
auto path = HttpUtils::UrlPathDecode(ctx.path);
|
||||
auto path = ((VFSPath)HttpUtils::UrlPathDecode(ctx.path)).CollapseRelativeParents();
|
||||
|
||||
|
||||
if(this->vfs->DirectoryExists(path))
|
||||
{
|
||||
|
||||
@@ -515,8 +515,7 @@ namespace Tesses::Framework::Http
|
||||
{
|
||||
if(ct.find("multipart/form-data") != 0)
|
||||
{
|
||||
std::cout << "Not form data" << std::endl;
|
||||
return;
|
||||
throw std::runtime_error("Not form data");
|
||||
}
|
||||
auto res = ct.find("boundary=");
|
||||
if(res == std::string::npos) return;
|
||||
@@ -581,9 +580,10 @@ namespace Tesses::Framework::Http
|
||||
fflush(stdout);
|
||||
if(http == nullptr || server == nullptr) return;
|
||||
auto svr=this->server;
|
||||
auto serverPort = this->server->GetPort();
|
||||
auto http = this->http;
|
||||
TF_LOG("Before Creating Thread");
|
||||
thrd = new Threading::Thread([svr,http]()->void {
|
||||
thrd = new Threading::Thread([svr,http,serverPort]()->void {
|
||||
while(TF_IsRunning())
|
||||
{
|
||||
TF_LOG("after TF_IsRunning");
|
||||
@@ -599,9 +599,9 @@ namespace Tesses::Framework::Http
|
||||
return;
|
||||
}
|
||||
TF_LOG("Before entering socket thread");
|
||||
Threading::Thread thrd2([sock,http,ip,port]()->void {
|
||||
Threading::Thread thrd2([sock,http,ip,port,serverPort]()->void {
|
||||
TF_LOG("In thread to process");
|
||||
HttpServer::Process(sock,http,ip,port,false);
|
||||
HttpServer::Process(sock,http,ip,port,serverPort,false);
|
||||
TF_LOG("In thread after process");
|
||||
|
||||
});
|
||||
@@ -829,7 +829,8 @@ namespace Tesses::Framework::Http
|
||||
else
|
||||
{
|
||||
auto chunkedStream = this->OpenResponseStream();
|
||||
this->strm->CopyTo(chunkedStream);
|
||||
|
||||
strm->CopyTo(chunkedStream);
|
||||
|
||||
|
||||
}
|
||||
@@ -928,7 +929,7 @@ namespace Tesses::Framework::Http
|
||||
|
||||
return *this;
|
||||
}
|
||||
void HttpServer::Process(std::shared_ptr<Stream> strm, std::shared_ptr<IHttpServer> server, std::string ip, uint16_t port, bool encrypted)
|
||||
void HttpServer::Process(std::shared_ptr<Stream> strm, std::shared_ptr<IHttpServer> server, std::string ip, uint16_t port,uint16_t serverPort, bool encrypted)
|
||||
{
|
||||
TF_LOG("In process");
|
||||
while(true)
|
||||
@@ -939,6 +940,7 @@ namespace Tesses::Framework::Http
|
||||
ctx.ip = ip;
|
||||
ctx.port = port;
|
||||
ctx.encrypted = encrypted;
|
||||
ctx.serverPort = serverPort;
|
||||
try{
|
||||
bool firstLine = true;
|
||||
std::string line;
|
||||
@@ -1093,4 +1095,7 @@ namespace Tesses::Framework::Http
|
||||
path2 = path2 / path;
|
||||
return path2.CollapseRelativeParents().ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ namespace Tesses::Framework::Http
|
||||
{
|
||||
|
||||
len = std::min((size_t)(this->length - this->position), len);
|
||||
|
||||
if(len > 0)
|
||||
len = this->strm->Write(buff,len);
|
||||
this->position += len;
|
||||
@@ -131,6 +132,7 @@ namespace Tesses::Framework::Http
|
||||
}
|
||||
else
|
||||
{
|
||||
if(len == 0) return 0;
|
||||
if(this->http1_1)
|
||||
{
|
||||
std::stringstream strm;
|
||||
|
||||
@@ -65,7 +65,7 @@ bool MountableServer::Handle(ServerContext& ctx)
|
||||
}
|
||||
}
|
||||
ctx.path=oldPath;
|
||||
if(this->root != nullptr && this->root->Handle(ctx)) return true;
|
||||
if(this->root && this->root->Handle(ctx)) return true;
|
||||
return false;
|
||||
}
|
||||
MountableServer::~MountableServer()
|
||||
|
||||
@@ -180,8 +180,9 @@ namespace Tesses::Framework::Platform {
|
||||
#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
return 0;
|
||||
#else
|
||||
if(this->strm < 0 || this->eos && writing) return 0;
|
||||
|
||||
if(this->strm < 0 || this->eos && writing) return 0;
|
||||
|
||||
auto r = read(this->strm,buff,sz);
|
||||
if(r == -1) return 0;
|
||||
if(r == 0 && sz != 0) { this->eos=true; return 0;}
|
||||
|
||||
@@ -129,9 +129,16 @@ namespace Tesses::Framework::Streams {
|
||||
|
||||
#else
|
||||
struct ifaddrs *ifAddrStruct = NULL;
|
||||
getifaddrs(&ifAddrStruct);
|
||||
errno = 0;
|
||||
if(getifaddrs(&ifAddrStruct) == -1)
|
||||
{
|
||||
|
||||
freeifaddrs(ifAddrStruct);
|
||||
return {};
|
||||
}
|
||||
for (struct ifaddrs *ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
if (ifa->ifa_addr->sa_family == AF_INET) { // IPv4
|
||||
|
||||
ipConfig.push_back(std::pair<std::string,std::string>(ifa->ifa_name, StringifyIP(ifa->ifa_addr)));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "TessesFramework/Streams/Stream.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Tesses::Framework::Streams {
|
||||
int32_t Stream::ReadByte()
|
||||
@@ -108,6 +109,7 @@ namespace Tesses::Framework::Streams {
|
||||
read = (size_t)std::min(len-offset,(uint64_t)buffSize);
|
||||
|
||||
read = this->Read(buffer,read);
|
||||
|
||||
strm->WriteBlock(buffer, read);
|
||||
|
||||
offset += read;
|
||||
@@ -122,8 +124,6 @@ namespace Tesses::Framework::Streams {
|
||||
{
|
||||
size_t read;
|
||||
uint8_t* buffer = new uint8_t[buffSize];
|
||||
|
||||
|
||||
do {
|
||||
read = this->Read(buffer,buffSize);
|
||||
strm->WriteBlock(buffer, read);
|
||||
|
||||
Reference in New Issue
Block a user