Hlavní navigace

Squeak: návrat do budoucnosti (17)

Pavel Křivánek 1. 6. 2004

Síťové technologie jsou jednou z oblastí, ve kterých Squeak nachází snadno praktické uplatnění. Dnes se na ně zaměříme. Ukážeme si základy práce se sokety, postavíme si nad nimi primitivní webový server, naučíme se stahovat soubory, stranou nezůstanou ani protokoly POP3, SMTP a FTP. Na závěr si krátce představíme další internetové a databázové technologie postavené na Squeak Smalltalku.

Základem komunikace s okolím prostřednictvím síťového rozhraní jsou proudy a sokety. O proudech jsme hovořili v předminulém dílu, dnes si představíme sokety, které implementuje třída Socket. S ní spolupracuje specializovaná proudová třída SocketStream.

Klient

Správa klienta je jednoduchá. Nejdříve se soket pro TCP připojení vytvoří standardním konstruktorem new (pro UDP připojení konstruktorem newUDP). Dále se provede připojení k serveru, přičemž se specifikuje adresa a port. Poté se se soketem asociuje proud a následně již můžeme přistoupit ke vzájemné komunikaci prostřednictvím běžných proudových operací.

| socket stream |
socket := Socket new.
socket
  connectTo: (NetNameResolver localHostAddress)
  port: 7654.

stream := SocketStream on: socket.
stream nextPutAll: 'Hello world'.
stream close.

Při uzavření proudu dojde současně i k uzavření příslušného soketu.

Adresace

Práci s IP adresami a jmény počítačů nám usnadňuje třída NetNameResolver. Umožňuje zjišťovat informace jak o hostitelském systému, tak o vzdálených počítačích. IP adresy jsou čtyřprvkové instance třídy ByteArray. Vzájemné převody z/do různých formátů adres nejsou problém.

NetNameResolver addressForName: 'root.cz'
        ->  a ByteArray(81 31 5 5)
NetNameResolver addressForName: '81.31.5.5'
        ->  a ByteArray(81 31 5 5)
NetNameResolver localHostAddress
        ->  a ByteArray(192 168 0 1)
NetNameResolver localAddressString
        ->  '192.168.0.1'
NetNameResolver localHostName
        ->  'wallace.comtalk.net'
NetNameResolver
        nameForAddress: #(81 31 5 5) asByteArray
        timeout: 60
 -> 'centaurus.4web.cz'

Časové limity se pro většinu těchto metod dají určit explicitně. Standardně je použita jedna minuta.

Server

Jak se používá připojení na straně serveru, si ukážeme na triviálním příkladě webového serveru. Nejdříve si pro něj vytvoříme třídu. Je extrémně zjednodušený, takže nebudeme potřebovat ani žádné instanční proměnné.

Object subclass: #WebServer
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Networking'

V metatřídě si nadefinujeme konstruktor start (server se bude pouštět příkazem WebServer start)

start
  | queue connection |

  queue := ConnectionQueue
    portNumber: 80
    queueLength: 50.

  [
    [
      connection := queue getConnectionOrNil.
      connection
        ifNotNil: [ self new serve: connection ].
      Processor yield
    ] repeat
  ] fork.

Připojení serverového soketu probíhá tak, že se nechá naslouchat na příslušném portu (zpráva listenOn:). Jestliže se k serveru připojí klient, musíme jej obsloužit (nejlépe v separátním vlákně) a naslouchat dále. To nám usnadňuje třída ConnectionQueue, která v procesu s poměrně vysokou prioritou sbírá na určeném portu připojení a řadí je do fronty, odkud si je můžeme vybírat a postupně obsluhovat. V našem případě tak činíme v nekonečné smyčce běžící v separátním vlákně. Při vytvořeníConnec­tionQueue se specifikuje naslouchaný port a maximální počet připojení čekajících ve frontě. Příkaz Processor yield jsme použili pro lepší rozložení zátěže.

Obsluha klienta je prostá. Každého budeme obsluhovat ve zvláštním vlákně. Nad dodaným soketem si vytvoříme proud a vybereme si z něm HTTP hlavičku (končí prázdným řádkem). Podle obsahu hlavičky klientovi odešleme odpověď, což bývá většinou HTML stránka.

serve: aConnection
  [
    | stream head |
    [
      stream := SocketStream on: aConnection.
      head := stream upToAll: (String crlf, String crlf).
      stream nextPutAll: (self response: head).
    ] ensure: [ stream ifNotNil: [ stream close ] ].
  ] fork. 

My mu vrátíme řetězec obsahující aktuální čas serveru.

response: head
  ^ 'Powered by Smalltalk',
  String crlf,
  'Current time is: ', Time now asString.

Aby to dělalo něco alespoň trochu užitečného, zpřístupníme statické HTML stránky. Přijmeme od klienta HTTP hlavičku, z níž vykousneme druhé slovo, které představuje URL pro lokální zdroj. Tedy pokud uživatel zadal do svého prohlížeče např. adresu /clanek/2208, bude tato URL obsahovat řetězec ‚/clanek/2208‘. První slovo je druh požadavku (GET, POST atd.). My ještě odřízneme parametry stránky, které se uvádějí za oddělující otazník. Pokud uživatel nezadal žádný lokální zdroj, napsal tedy pouze http://www.root.cz, obdržíme v hlavičce ve specifikaci lokálního zdroje pouze znak'/'. V tom případě použijeme standardně stránku index.html. Z URL si vytvoříme fyzickou cestu ke zdroji (obsah stránek máme v adresáři /inetpub), prověříme existenci takového souboru a jeho obsah vrátíme uživateli.

response: head

  | url path |

  url := (head findTokens: ' ') second upTo: $?.
  (url = '/') ifTrue: [ url := '/index.html' ].
  path := '/inetpub', url.
  (StandardFileStream isAFileNamed: path)
    ifFalse: [^ 'Resource not found',String crlf].

  ^ FileStream readOnlyFileNamed: path .

V tomto okamžiku již klient může prohlížet statické stránky včetně obrázků apod. Náš server je sice poměrně snadno DoSnutelný a bezpečný jako procházka po Traalu bez ručníku na očích, ale jako demonstrační příklad snad vyhovuje.

Přirozeně se jedná o nošení dříví do lesa. Pro Squeak existuje několik webových serverů, z nichž nejvíce vyčnívá projekt Comanche (KomHttpServer), který toho skutečně nenabízí málo a slouží jako základ pro další pokročilé webové platformy.

HTTP klient

Ve Squeaku existuje program jménem Scamper, což je zcela samostatný webový prohlížeč. Nachází se ve velmi raném stádiu vývoje a korektně dokáže zobrazit jen velmi jednoduché stránky. Na surfování rovnou zapomeňte, ale i tak může posloužit přinejmenším jako pokladnice příkladů pro práci HTTP klienta, který si dokáže z Internetu ledasco stáhnout a nějak to přelouskat.

Pro obsluhu vzdálených zdrojů slouží třída HttpUrl. Nejjednoduššeji se konstruuje přímo z řetězce adresy metodou asUrl třídy String.

'/clanek/2208' asUrl

Třída HttpUrl má nejednu zajímavou metodu. Nejčastěji asi využijete metodu retrieveContents, která adresovaný zdroj stáhne a vrátí instanci třídy MIMEDocument. Z ní kromě dodatečných informací o typu dokumentu můžeme získat samozřejmě i její obsah metodou contents, vytvořit si nad ní proud a podobně. Stažení a uložení HTML stránky či libovolného jiného dokumentu do souboru by pak mohlo vypadat například takto:

(FileStream newFileNamed: 'page.html')
  nextPutAll: ('/clanek/2208'
    asUrl retrieveContents contents);
  close.

Třída HttpUrl toho umí samozřejmě i více. Občas se může hodit např. derelativizace odkazu z dané adresy. Je-li např. na stránce /clanek/2208 uveden odkaz na dokument ‚../index.html‘, pak metoda newFromRelativeText vrátí jeho absolutní adresu. Absolutní adresy zůstavají beze změny.

'/clanek/2208'
        asUrl newFromRelativeText: 'index.html'
  ->'/clanek/index.html'

'/clanek/2208'
        asUrl newFromRelativeText: '/index.html'
  ->' /index.html'

HTML a XML

Pokud již stáhneme z Internetu nějaký dokument, často jej potřebujeme zanalyzovat. U HTML stránek nám výrazně pomůže např. třída HtmlTokenizer, která dokáže dokument ze vstupního proudu zpracovat tag po tagu, analyzovat u něj jednotlivé atributy atd. Například jednoduchý prográmek, který ze vstupního proudu s obsahem HTML stránky (proměnná page) získá všechny odkazy a uloží je do výsledné kolekce (proměnná result), odkud mohou být dle libosti dále derelativizovány a staženy, může vypadat například takto:

| result token anchor tokenizer |

result := OrderedCollection new.
tokenizer := HtmlTokenizer on: page.

[ token := tokenizer next.  token = nil ] whileFalse: [
  token isTag ifTrue: [
    (token name = 'a') ifTrue: [
      anchor := (token attribs at: #href ifAbsent: []).
      anchor ifNotNil: [ result add: anchor].
    ].
  ].
]. 

Pro syntaktickou analýzu XML souborů standardní distribuce Squeaku prostředky nemá, nicméně existuje například analyzátor YAXO stáhnutelný ze SqueakMap pod stejnou licencí, jakou má Squeak. Je neméně jednoduše použitelný a i on rovněž slouží jako základ pro nejeden XML serializer a podobné nadstavby. Osobně jsem jej několikrát v praxi prověřil na XML souborech o velikostech až několik MB a s problémy jsem se nesetkal.

SMTP, POP3

Squeak obsahuje i třídy poskytující služby poštovního klienta. Pravděpodobně nejčastěji upotřebíte možnost zaslat jednoduchý textový e-mail např. s informacemi o stavu vašeho serveru, zaslání obsahu webového formuláře apod. Při odesílání dopisu je samozřejmě nutné specifikovat odchozí SMTP server.

SMTPClient
  deliverMailFrom: 'odesilatel@server.cz'
  to: #('prijemce@server.cz')
  text:
'From: odesilatel@server.cz
To: prijemce@server.cz
Subject: Predmet

Obsah zprávy
'
  usingServer: 'smtp.etmail.cz'

Krom odesílání zpráv nečiní Squeaku žádné potíže pracovat jako POP3 klient (třída POP3Client). Squeak obsahuje i funkčního grafického poštovního klienta jménem Celeste, který si poradí i s odesíláním a přijímáním příloh, takže pokud tuto funkčnost budete ve svých projektech vyžadovat, můžete jej vesele vykrádat.

FTP

Squeak standardně využívá komunikaci s FTP servery pro stahování svých aktualizací apod. Základní činnosti zapouzdřuje třída FTPClient. Jejích služeb využívá třída s příjemnějším rozhraním jménem ServerDirectory. Obdržení výpisu adresáře na serveru může vypadat např. takto:

(ServerDirectory on: 'comtalk.net')
  user: 'Anonymous';
  password: 'yourName@company.com';
  getDirectory

Práce se soubory se pak provádí např. pomocí příkazu getFileNamed:, putFile:named:, getFileList apod.

Databáze

Smalltalk je sám o sobě objektová databáze, nicméně bez zaručené perzistence a transakcí. Ovšem ne náhodou tvoří základ pro profesionální objektové databázové systémy, jako je např. GemStone/S., který především ve spojitosti s VisualWorks nabízí vynikající platformu pro skutečně komplexní náročné informační systémy.

Ovšem i na Squeaku se rodí nejedna objektová databáze (Magma, MinneStore, OmniBase apod.) a některé z nich vypadají velmi slibně. Pokud se rozhodnete svěřit svá data nějaké běžné relační databázi, jistě uvítáte ovladače pro MySql, PostgreSQL, SQLite či ODBC. Ty jsou řešeny buď síťovou komunikací, nebo přes volání funkcí hostitelského systému (FFI).

Nutno podotknout, že i práce s relačními databázemi je ve Smalltalku velmi příjemná, protože díky nástrojům, jako je Inspector, máte na první pohled dokonalý přehled o struktuře a obsahu získaných dat, nehledě na možnost s nimi okamžitě dle libosti manipulovat a experimentovat.

V případech, kdy i relační databáze je příliš velký kanon, máte na výběr nejeden způsob řešení perzistence objektů. Např. extrémně jednoduchý, leč velmi pohodlně použitelný FileDictionary, či XML (de)serializer SIXX.

Bezpečnost

Pro Squeak existuje celá řada kryptografických algoritmů. Za zmínku stojí implementace hešování zpráv pomocí SHA obsažená ve standardní distribuci, která vám jistě usnadní autorizační mechanismy

SecureHashAlgorithm new hashMessage: 'heslo'.
  ->628022029064920457606303469569901564949872826602

Squeak v současnosti nenabízí nativní podporu pro SSL. V případě potřeby zabezpečeného připojení se doporučuje využívat např. Stunnel.

Některá zabezpečení lze řešit přímo na úrovni virtuálního stroje. Např. lze úplně zakázat přístup k souborům, síti apod. To může být v mnoha případech účinější než nastavení práv hostitelského systému. V extrémních případech vám samozřejmě nic nebrání si zdrojové soubory VM upravit dle libosti tak, aby přesně odpovídaly vašim bezpečnostním potřebám.

Další projekty

Na co Squeak teprve čeká, je kvalitní podpora webových služeb. SOAP již implementován je, WSDL se připravuje. V případě, že vás láká práce s webovými službami ve Smalltalku, budete muset prozatím sáhnout po některé komerční implementaci.

Jak jsme si řekli, nad webovým serverem Comanche je postaveno několik aplikací a frameworků. Nejpoužívanější z nich jsou HttpView2, SeaSide a Swiki.

HttpView2 je jednoduchý intuitivní framework poskytující základní prostředí pro tvorbu dynamických webových stránek. Stránky jsou většinou generovány přímo ze zdrojové metody pomocí zasílání zpráv konstrukčnímu objektu HTML stránky.

Podstatně propracovanější je SeaSide, což je mocné objektové prostředí pro tvorbu webových aplikací podporující sessions, view-states apod.

Swiki pak je již hotová webová aplikace pro uživatelsky editovatelné stránky s podporou vyhledávání, historie, zabezpečení, změn vzhledu apod. Její velkou předností je, že ji může okamžitě používat i člověk, který o Smalltalku v životě neslyšel a nikdy v něm neviděl jediný napsaný řádek kódu.

Webové technologie ve Squeaku jsou velmi zajímavá a progresivní problematika a dalece přesahují rozsah tohoto úvodního seriálu. Spíše by si zasloužily seriál samostatný. To jsem se už vůbec nezmínil o možnostech distribuovaných objektů apod.

Potenciál, který při budování webových aplikací Smalltalk má, si jistě snadno domyslí každý, kdo si uvědomí, jak snadno v něm jde např. ze zdrojové stránky vydolovat kód, vytvořit pro tuto stránku samostatnou třídu, přeložit a přidat do ní metody, vytvářet a rušit její instance, případně ji zase ze systému smazat atd. Podobně je např. v Morphicu řešena práce s tzv. hráči (Players) pro návrh uživatelského rozhraní. O možnostech tvorby dynamických stránek na bázi prototypů ani nemluvě. To je oblast, kde si staticky typované vysokoúrovňové jazyky už ani neškrtnou.

Našli jste v článku chybu?

6. 6. 2004 20:13

vj (neregistrovaný)

Primitiva samozrejme nesmi byt blokujici, ale cekaci smycka muze mimo uvedenych moznosti taky cekat na semafor, kteremu ve vhodny okamzik posle signal primo VM.

3. 6. 2004 14:13

Pavel Křivánek (neregistrovaný)

Primitiva pro naslouchání na soketu je neblokující, tzn. že vždy je třeba existenci nového připojení cyklicky testovat. Viz např. ConnectionQueue>>listenLoop. V příkladu uvedeném v článku je použití ConnectionQueue skoro zbytečné, protože ta se používá především v situacích, kdy všechna spojení obsluhujeme jedno za druhým v jednom procesu. Místo Processor yield by šlo použít i třídu Delay.

Pro smalltalkovskou architekturu vlastně ani není jiné cesty, protože primitivy se provádí atomic…

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Jak koupit Mikuláše a nenaletět

Jak koupit Mikuláše a nenaletět

Vitalia.cz: Vláknina: Rozpustná, nebo nerozpustná?

Vláknina: Rozpustná, nebo nerozpustná?

DigiZone.cz: Další dva kanály nabídnou HbbTV

Další dva kanály nabídnou HbbTV

Vitalia.cz: I církev dnes vyrábí potraviny

I církev dnes vyrábí potraviny

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Tesco: Chudá rodina si koupí levné polské kuře

Tesco: Chudá rodina si koupí levné polské kuře

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

DigiZone.cz: Flix TV má set-top box s HEVC

Flix TV má set-top box s HEVC

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase