Serwer http/https z pełną obsługą strony www + obrazki + style + jquery + czcionki

0

Witam,
Mam taki cel: Zrobić serwer www (http/https) w delphi za pomocą indy 10.
Powinien obsługiwać protokół http / https, poprawnie (klasy ala apatche) obsługiwać stronę www + obrazki + js (jquery) + css.

Niestety nie udało mi się znaleźć tutka, który by przedstawiał jak taki serwerek w delphi zrobić. W każdym coś nie działa albo nie ładują się obrazki albo css albo częściowo style się ładują a częściowo nie itp.

Na razie mam tyle kodu (nie wiele:/). Strona .htm się wyświetla i nic więcej nie działa.

Moje początkowe pytanie : Jak dodać poprawną obsługę do serwera http obrazków plików js css w tym czcionek itp. tak aby na przykład można było na nim uruchomić taki framework bootstrap ?

 
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
  IdComponent, IdCustomTCPServer, IdCustomHTTPServer, IdHTTPServer, IdContext;

type
  TForm1 = class(TForm)
    IdHTTPServer1: TIdHTTPServer;
    ButtonHTTPServerOn: TButton;
    ButtonHTTPServerOff: TButton;
    procedure ButtonHTTPServerOffClick(Sender: TObject);
    procedure ButtonHTTPServerOnClick(Sender: TObject);
    procedure IdHTTPServer1CommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ButtonHTTPServerOffClick(Sender: TObject);
begin
Form1.IdHTTPServer1.Active:=False;
end;

procedure TForm1.ButtonHTTPServerOnClick(Sender: TObject);
begin
Form1.IdHTTPServer1.DefaultPort:=80;
Form1.IdHTTPServer1.Active:=True;
end;

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var Stream: TFileStream;
      Groesse: String;
begin
  if ARequestinfo.Document='/' then
  begin
     //AResponseinfo.ContentType:='text/html';
     Stream:=TFileStream.Create('htdocs/index.htm', fmOpenRead or fmShareDenyWrite);
      //AResponseInfo.ContentText :='<html><head><title>Error</title></head><body><h1>Access denied</h1></body></html>';
     AResponseinfo.ContentStream:=Stream;
     setlength(Groesse, Stream.Size);
     Stream.Read(Groesse[1], Stream.Size);
  end;


end;

end.
0

Najlepiej działający na tej stronie jest "Eliza Web Server", ale nie działa na nim framework bootstrap, prawdopodobnie problem z css.
"Demos are built using Delphi 7" lekko starawe te dema są.

2

Oj człowieku ogarnij temat... chciałeś gotowca? Tak dobrze nie ma obsługa plików się sama nie napisze trzeba zwracać odpowiednie nagłówki (min. odpowiednie ContentType np. dla plików CSS) wszystko co trzeba robisz w obsłudze żądań (OnCommandGet) np:

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  LFilename: string;
  LPathname: string;
  Ext: string;
begin
  LFilename := ARequestInfo.Document;
  if LFilename = '/' then
    LFilename := '/index.html';
  LPathname := FHTMLDir + LFilename;
  if FileExists(LPathname) then
  begin
    AResponseInfo.ContentStream := TFileStream.Create(LPathname, fmOpenRead + fmShareDenyWrite);
    if AResponseInfo.ContentStream <> nil then
    begin
      AResponseInfo.ResponseNo:= 200;
      Ext:=  LowerCase(ExtractFileExt(LPathname));
      if Ext = '.css' then
        AResponseInfo.ContentType:= 'text/css';
      exit;
    end;
  end;
  AResponseInfo.ResponseNo:= 404; //wala mnie bledy bo to uproszczona obsluga wiec w razie czego 404 i czesc
end;
0

Dzięki teraz działa tak jak powinno :)

0

Teraz dopadł mnie następny tym razem mały problemik:

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var
  LFilename: string;
  LPathname: string;
  Ext: string;
begin
  LFilename := ARequestInfo.Document;  //Co chce dostac przegladarka, jaki plik
  memo1.Lines.Append(LFilename);
  if LFilename = '/' then
    LFilename := '/index.htm';
  LPathname := FHTMLDir + LFilename;
  if FileExists(LPathname) or (LFilename = '/index.htm')  then
  begin
    if LFilename = '/index.htm' then
       AResponseInfo.ContentStream:=strumien
      else
        AResponseInfo.ContentStream := TFileStream.Create(LPathname, fmOpenRead + fmShareDenyWrite);
    if AResponseInfo.ContentStream <> nil then
    begin
      AResponseInfo.ResponseNo:= 200;
      Ext:=  LowerCase(ExtractFileExt(LPathname));
      if Ext = '.css' then
        AResponseInfo.ContentType:= 'text/css';
      exit;
    end;
  end;
  AResponseInfo.ResponseNo:= 404; //wala mnie bledy bo to uproszczona obsluga wiec w razie czego 404 i czesc
end;

Prawdopodobnie problem leży w przypisaniu tego streama:

       AResponseInfo.ContentStream:=strumien

Ogólnie uruchamiam serwer http wczytuje plik index.htm do streama (strumien) (mam możliwość wczytania do memo i faktycznie tam jest ten plik)

usuwam plik index.htm z serwera odpalam przeglądarkę wszystko ładnie się wczytuje po czym serwer http się zawiesza :/

Prawdopodobnie przypisanie tego streama do AResponseInfo.ContentStream jest błędne?

0

Nie tędy droga, oj nie tędy...
Polecam zapoznanie się z tym:
http://blog.synopse.info/post/2014/10/24/MVC-MVVM-WebApp-mORMot

0

Ogólnie mój model tej aplikacji jest taki:

Gość otwiera stronę wpisując jakiś tam adres www. (Serwer http) przesyła mu niezbędną formatkę (html + style + fonty + js).

Ta formatka nie odświeża się pobierając kolejne dane z (Serwer http) tylko łączy z innym serwerem (DataSnap Server) za pomocą JSON, który to się komunikuje również z bazą danych itp.

(Serwer http) służy tylko do pobrania formatek, aplikacji webowej do komunikacji, natomiast dane pobierane są z innego serwera asynchronicznie.

Taki to model sobie wymyśliłem, być może są jeszcze lepsze modele.

Na początku będą one obsługiwać faucet bitcoin. Ogólnie musi to być odporne na różne próby ataków i prób wyłudzeń bitcoinów przez różne boty itp. Kiedyś założyłem faucet bitcoin na microfaucet ale system nie jest bezpieczny co skończyło się opróżniem faucetu w ciągu 2 godzin. Oczywiście były standardowe zabezpieczenia typu unikalne ip i kody obrazkowe.

W przypadku własnego rozwiązania z własnym portfelem bitcoin wszystkie operacje odbywają się w tym samym programie portfelu bitcoin i mogę kontrolować legalność zdobywana bitcoinów przez użytkowników i eliminować potencjalnych oszustów próbujących wykorzystać luki w systemie lub boty.

0

Nie znam się na bitcoinach ale jedno jest pewne aby napisać BEZPIECZNY serwer z prawdziwego zdarzenia trzeba mieć potężną wiedzę i znać doskonale nie tylko protokół HTTP(S) (aby dobrze obsłużyć wszystkie możliwe żądania) ale również trzeba zadbać o bezpieczeństwo a z tym nawet (głównie z winy nieodpowiednich ustawień) na profesjonalnych serwerach dość ciężko i nie chcę Cię martwić ale gdyby takie cudo szło naklikać w 3 dni rozwiązałbyś problemy milionów ludzi. Myślałem że chodzi o jakiś prostacki pseudo serwer do obsługi webowej wersji jakiejś prostej aplikacji. Na Twoim miejscu jednak bym starał się skorzystać z gotowego serwera.

0

Dlaczego takie podstawienie strumienia nie działa: TMemoryStream()

Raz zadziała i potem ponowna próba odczytania powoduje jakiś błąd pamięci

AResponseInfo.ContentStream := strumien;

A te poniżej działa prawidłowo: (TFileStream)

AResponseInfo.ContentStream := TFileStream.Create(LPathname, fmOpenRead + fmShareDenyWrite);

Oraz takie podstawienie też działa prawidłowo (wczytanie pliku do strumienia):

strumien.LoadFromFile(FHTMLDir + '/index.htm');

To rozwiązało sprawę :)

AResponseInfo.ContentStream := TMemoryStream.Create();
strumien.SaveToStream(AResponseInfo.ContentStream);
0

Teraz inny problemik, czy w delphi można stworzyć TMemoryStream za pomocą funkcji / procedury o zasięgu globalnym. Ponieważ nie wiem ile będę ich potrzebował przed uruchomieniem programu. ?

0
alien1983 napisał(a):

Dlaczego takie podstawienie strumienia nie działa: TMemoryStream()

Raz zadziała i potem ponowna próba odczytania powoduje jakiś błąd pamięci

AResponseInfo.ContentStream := strumien;

Bo za pierwszym razem w zmiennej 'strumien' masz ważną referencję na strumień, a w drugim przypadku już nie.

A te poniżej działa prawidłowo: (TFileStream)

AResponseInfo.ContentStream := TFileStream.Create(LPathname, fmOpenRead + fmShareDenyWrite);
   

Bo za każdym razem tworzysz obiekt strumienia i przypisujesz jego referencję.

Oraz takie podstawienie też działa prawidłowo (wczytanie pliku do strumienia):

strumien.LoadFromFile(FHTMLDir + '/index.htm');

Bo to odpowiednik drugiego sposobu

To rozwiązało sprawę :)

AResponseInfo.ContentStream := TMemoryStream.Create();
       strumien.SaveToStream(AResponseInfo.ContentStream);

Taa...
A teraz zbadaj wycieki pamięci.

0
alien1983 napisał(a):

Teraz inny problemik, czy w delphi można stworzyć TMemoryStream za pomocą funkcji / procedury o zasięgu globalnym. Ponieważ nie wiem ile będę ich potrzebował przed uruchomieniem programu. ?

Można, tylko po co?
Wiesz że ta funkcja wyglądać będzie tak:

function GetMStream : TMemoryStream;
begin
  Result := TmemoryStream.Create;
end;

A więc nie ma to sensu...

Poza tym, twoje boje są naprawdę zabawne ;-)

0

ok o to mi chodziło teraz działa:

t : array of TMemoryStream;

1 użytkowników online, w tym zalogowanych: 0, gości: 1