← Zpět na články

Začínáme s AISL

Pokud potřebujete spolehlivé a výkonné backendové řešení, na volbě technologií velmi záleží. Hlavní výhodou AISL je možnost vytvořit aplikaci přímo nad HTTP streamem a průběžně reagovat na jednotlivé hlavičky a části obsahu. Jde o bezpečný přístup, který výrazně šetří procesorový čas.

Běžný backend je aplikace, která čeká, až jí HTTP server předá zpracovaný HTTP požadavek prostřednictvím některého rozhraní, například WSGI, FastCGI nebo CGI.

Běžný backend

Než může backend data z HTTP požadavku zpracovat, musejí být všechna uložena do mezipaměti v paměti nebo na disku. Objem odesílaných dat přitom může dosahovat gigabajtů a backend bude čekat, dokud je webový server nepřijme celá, i když je v danou chvíli vůbec nepotřebuje.

Aplikace založené na AISL používají jiný přístup. Backend je především sám webovým serverem a nezpracovaný HTTP požadavek je přímo očekávaným datovým rozhraním. Backend tedy nemusí se zpracováním dat čekat až do konce HTTP požadavku.

Backend AISL

První kroky

Vyzkoušejme si to vytvořením jednoduché aplikace Hello World. V tomto i následujících návodech se nazývá Minion, svou aplikaci však můžete pojmenovat libovolně. Nejprve vytvořte novou složku, inicializujte v ní repozitář Git a přidejte podsložku src.

mkdir minion
cd minion
git init
mkdir src

Dalším krokem je instalace samotného AISL jako submodulu:

git submodule add https://github.com/lowenware/aisl.git

Vývoj

V oblíbeném textovém editoru nebo IDE vytvořte v podsložce src soubor main.c. Následující úryvky kódu do něj postupně přidávejte v uvedeném pořadí.

Nejprve vložte potřebné hlavičkové soubory standardní knihovny a zastřešující hlavičkový soubor AISL. Jde o jediný hlavičkový soubor této knihovny, který je třeba do projektu vložit.

#include <stdlib.h>
#include <stdio.h>

#include <aisl/aisl.h>

Nyní deklarujte callback pro události vyvolané AISL. Implementaci doplníme později.

static void
minion_callback(const struct aisl_evt *evt, void *p_ctx);

Definujte strukturu, která AISL určí, na kterém rozhraní a portu se má HTTP server spustit. V našem případě jde o port 8081 na všech síťových rozhraních.

static const struct aisl_cfg_srv m_srv = {
 .host = "0.0.0.0",
 .port = 8081
};

Pomocí makra AISL_CFG_DEFAULTS, ukazatele na strukturu m_srv a callbacku z předchozích kroků definujte další konfigurační strukturu.

static const struct aisl_cfg m_cfg = {
 AISL_CFG_DEFAULTS,
 .srv = &m_srv,
 .srv_cnt = 1,
 .callback = minion_callback,
};

Nyní přichází na řadu funkce main.

int
main(int argc, char **argv)
{
 AislInstance aisl;
 AislStatus status;

 if ((aisl = aisl_new(&m_cfg)) != NULL) {
  fprintf(stdout, "entering main loop\n");
  for (;;) {
   status = aisl_run_cycle(aisl);
   if (status != AISL_SUCCESS)
    aisl_sleep(aisl, 500);
  }
  aisl_free(aisl);
 } else {
  fprintf(stderr, "could not initialize AISL\n");
  return -1;
 }
 return 0;
}

Funkce aisl_new vytvoří instanci enginu AISL podle zadané konfigurace. Instance je ukazatel na paměť, kterou je při ukončení třeba uvolnit pomocí aisl_free. Funkce aisl_run_cycle provede všechny rutiny a vyvolá události, které zpracuje callback. V neposlední řadě funkce aisl_sleep pozastaví běh na 500 ms nebo do okamžiku, kdy dojde k aktivitě na síťových socketech.

Poslední chybějící částí je callback deklarovaný na samém začátku. Nyní jej implementujeme.

static void
minion_callback(const struct aisl_evt *evt, void *p_ctx)
{
 AislStream s;
 const char html[] =
  "<html>"
   "<head>"
    "<title>Hello World</title>"
    "<style>"
     "body {width: 100%; height: 100%}"
     "h1 {font-size: 4em}"
     ".hello-world {"
      "position: absolute;"
      "top: 50%;"
      "left: 50%;"
      "width: 640px;"
      "height:200px;"
      "margin: -100px 0 0 -320px;"
      "text-align: center;"
     "}"
    "</style>"
   "</head>"
   "<body>"
    "<div class=\"hello-world\">"
     "<h1>Hello World</h1>"
     "<p>I am your new Minion</p>"
    "</div>"
   "</body>"
  "</html>";

 if (evt->code != AISL_EVENT_STREAM_REQUEST)
  return;

 s = evt->source;

 if (aisl_response(s, AISL_HTTP_OK, sizeof(html) - 1) == AISL_SUCCESS)
 {
  if (aisl_write(s, html, sizeof(html) - 1) != -1) {
   aisl_flush(s);
   return;
  }
 }

 aisl_reject(s);
}

Callback přijímá dva argumenty:

  • const struct aisl_evt *evt: ukazatel na strukturu s podrobnostmi události,
  • void *p_ctx: ukazatel na uživatelská data, který zatím nepoužíváme.

Konstanta html obsahuje statické tělo HTML, které se odešle jako odpověď na libovolný HTTP požadavek. Zpracovává se pouze událost AISL_EVENT_STREAM_REQUEST. Události tohoto typu vždy poskytují ve vlastnosti source objekt AislStream, který je nutný k sestavení odpovědi.

Každá odpověď musí začít voláním funkce aisl_response, která přijímá instanci AislStream, stavový kód HTTP a velikost odpovědi. AISL_HTTP_OK odpovídá stavu HTTP s kódem 200, zatímco sizeof(html)-1 je délka řetězce html bez koncového nulového znaku. Pokud délku dat odpovědi neznáme a musíme ji vypočítat za běhu, lze místo konkrétní délky předat speciální makro AISL_AUTO_LENGTH.

Funkce aisl_write zapíše do AislStream data zadané délky.

Volání aisl_flush sdělí enginu, že je odpověď připravena a lze ji odeslat klientovi.

Pokud se cokoli pokazí, například když klient ukončí spojení s aplikací, je vhodné klienta okamžitě odmítnout pomocí aisl_reject.

Překlad

Kód aplikace je v podstatě hotový, nyní jej však musíme přeložit. V kořenové složce aplikace vytvořme Makefile.


.PHONY: dirs clean

default: minion

minion: aisl/build/libaisl.a dirs
 gcc -I./aisl/include src/main.c aisl/build/libaisl.a -o build/$@

aisl/build/libaisl.a:
 cd aisl && make AISL_WITH_SSL=0 libaisl.a

dirs:
 mkdir -p build

clean:
 rm -Rf build

Aplikaci přeložte pomocí make:

make

Poté ji spusťte v terminálu:

$ ./build/minion
entering main loop

Když nyní v prohlížeči otevřete http://localhost:8081/, měli byste vidět přibližně toto:

Aplikace AISL Hello World

Užitečné odkazy

Komentáře