TBrookHTTPServer
brook是c写的,以dll供delphi和lazarus调用。
unit Brook.HTTPServer;
//cxg 2023-2-12
interface
uses
sock.router, System.JSON, Json.help, keyValue.serialize, api.router,
BrookHTTPRequest, BrookHTTPResponse, BrookHTTPServer, global, IniFiles,
Classes, SysUtils;
var
BinApis: TJSONObject; //二进制API
RestApis: TJSONObject; //REST API,包括json,protobuf
const //content-type
cBin = 'application/octet-stream; charset=utf-8';
cJson = 'application/json; charset=utf-8';
cProtobuf = 'application/protobuf; charset=utf-8';
type
TbrookServer = class(TBrookHTTPServer)
protected
procedure DoRequest(ASender: TObject; ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse); override;
public
constructor Create; overload;
destructor Destroy; override;
end;
var
SERVER: TbrookServer;
implementation
function strof(const aBytes: TBytes): RawByteString; overload;
begin
SetLength(Result, Length(aBytes));
Move(aBytes[0], Result[1], Length(aBytes));
end;
function ReadJsonFile(const FileName: string): string;
begin
var f: TStringList := TStringList.Create;
f.LoadFromFile(FileName, TEncoding.UTF8);
Result := f.Text;
f.Free;
end;
procedure setHeader(const Ctxt: TBrookHTTPResponse);
begin
Ctxt.Headers.Add('Access-Control-Allow-Origin', '*');
Ctxt.Headers.Add('Access-Control-Allow-Methods', '*');
Ctxt.Headers.Add('Access-Control-Allow-Headers', '*');
end;
procedure router(const Ctxt: TBrookHTTPRequest; const Ctxt2: TBrookHTTPResponse); //brook router
begin
if Pos('/bin', Ctxt.path) > 0 then //二进制 API
begin
setHeader(Ctxt2);
var req: TSerialize := tserialize.Create;
var res: TSerialize := tserialize.Create;
if Ctxt.Payload.Length > 0 then
req.unMarshal(Ctxt.Payload.Content);
var url: string := Ctxt.path;
var arr: TArray<string> := url.Split(['/']);
var funcName: string := arr[2];
var ja: TJSONArray := BinApis.A['bin'];
for var i: Integer := 0 to ja.Count - 1 do
begin
var o: TJSONObject := ja.Items[i] as TJSONObject;
if o.S['funcname'] = funcName then
begin
RouterAPI(o.S['classname'], o.S['funcname'], [req, res]);
Break;
end;
end;
var r: TBytes := res.marshal2;
Ctxt2.SendBytes(r, Length(r), cBin, 200);
req.Free;
res.Free;
end
else if Pos('/rest', Ctxt.path) > 0 then //JSON API
begin
setHeader(Ctxt2);
var url: string := Ctxt.path;
var arr: TArray<string> := url.Split(['/']);
var resource: string := arr[2];
var req: TSerialize := tserialize.Create;
var res: TSerialize := tserialize.Create;
req.asStr['url'] := url;
var body: RawByteString;
if Ctxt.Payload.Length > 0 then
begin
body := strof(Ctxt.Payload.Content);
req.asStr['body'] := body;
end;
req.asStr['type'] := 'json';
var ja: TJSONArray := RestApis.A[resource];
for var i: Integer := 0 to ja.Count - 1 do
begin
var o: TJSONObject := ja.Items[i] as TJSONObject;
if o.S['method'] = Ctxt.Method then
begin
RouterAPI(o.S['classname'], o.S['funcname'], [req, res]);
Break;
end;
end;
Ctxt2.Send(UTF8Encode(res.asStr['res']), cJson, 200);
req.Free;
res.Free;
end
else if Pos('/protobuf', Ctxt.path) > 0 then //PROTOBUF API
begin
setHeader(Ctxt2);
var url: string := Ctxt.path;
var arr: TArray<string> := url.Split(['/']);
var resource: string := arr[2];
var req: TSerialize := tserialize.Create;
var res: TSerialize := tserialize.Create;
req.asStr['url'] := url;
if Ctxt.Payload.Length > 0 then
req.asBytes['body'] := Ctxt.Payload.Content;
req.asStr['type'] := 'protobuf';
var ja: TJSONArray := RestApis.A[resource];
for var i: Integer := 0 to ja.Count - 1 do
begin
var o: TJSONObject := ja.Items[i] as TJSONObject;
if o.S['method'] = Ctxt.Method then
begin
RouterAPI(o.S['classname'], o.S['funcname'], [req, res]);
Break;
end;
end;
var r: TBytes := res.asBytes['res'];
Ctxt2.SendBytes(r, Length(r), cProtobuf, 200);
req.Free;
res.Free;
end;
end;
constructor TbrookServer.Create;
var
ini: tinifile;
begin
SERVER := TbrookServer.Create(nil);
ini := TIniFile.Create(svrcfg);
ini.readinteger('config', 'httpport', 0);
var threadnum: Integer := ini.readinteger('config', 'threadnum', 32);
if threadnum > 256 then
threadnum := 256;
server.ThreadPoolSize := threadnum;
SERVER.Port :=ini.readinteger('config', 'httpport', 0);
ini.free;
SERVER.Open;
{$IFDEF console}
Writeln('New brook http server');
Writeln('Http port: ', SERVER.Port);
Writeln('Thread num: ', server.ThreadPoolSize);
{$ENDIF}
end;
destructor TbrookServer.Destroy;
begin
SERVER.Free;
inherited;
end;
procedure TbrookServer.DoRequest(ASender: TObject; ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
begin
router(ARequest, AResponse);
end;
initialization
binapis := TJSONObject.Create;
restapis := TJSONObject.Create;
binapis.Parse(ReadJsonFile(ExtractFilePath(ParamStr(0)) + 'binrouter.json'));
restapis.Parse(ReadJsonFile(ExtractFilePath(ParamStr(0)) + 'restrouter.json'));
finalization
FreeAndNil(binapis);
FreeAndNil(restapis);
end.