HTTP server so sieťou Netty

1. Prehľad

V tejto príručke sa chystáme implementovať jednoduchý horný server HTTP so sieťou, asynchrónny rámec, ktorý nám dáva flexibilitu pri vývoji sieťových aplikácií v prostredí Java.

2. Zavádzanie servera

Než začneme, mali by sme si byť vedomí základných konceptov Netty, ako sú kanál, obslužná rutina, kódovač a dekodér.

Tu skočíme priamo do bootstrapovania servera, ktorý je väčšinou rovnaký ako server s jednoduchým protokolom:

public class HttpServer {private int port; private static Logger logger = LoggerFactory.getLogger (HttpServer.class); // constructor // main method, same as simple protocol server public void run () throws Exception {... ServerBootstrap b = new ServerBootstrap (); b.group (bossGroup, workerGroup) .channel (NioServerSocketChannel.class) .handler (new LoggingHandler (LogLevel.INFO)) .childHandler (new ChannelInitializer () {@Override protected void initChannel (SocketChannel ch) vyvolá výnimku {ChannelPipeline p = chanel .pipeline (); p.addLast (nový HttpRequestDecoder ()); p.addLast (nový HttpResponseEncoder ()); p.addLast (nový CustomHttpServerHandler ());}}); ...}} 

Takže, tu len childHandler sa líši podľa protokolu, ktorý chceme implementovať, čo je pre nás HTTP.

Do potrubia servera pridávame tri obslužné programy:

  1. Netty HttpResponseEncoder - na serializáciu
  2. Netty HttpRequestDecoder - na deserializáciu
  3. Náš vlastný CustomHttpServerHandler - za definovanie správania nášho servera

Pozrime sa ďalej na posledného spracovateľa podrobne.

3. CustomHttpServerHandler

Úlohou nášho spracovateľa je spracovávať prichádzajúce údaje a posielať odpovede.

Poďme si to rozdeliť, aby sme pochopili jeho fungovanie.

3.1. Štruktúra psovoda

CustomHttpServerHandler rozširuje abstrakt Netty SimpleChannelInboundHandler a implementuje svoje metódy životného cyklu:

verejná trieda CustomHttpServerHandler rozširuje SimpleChannelInboundHandler {súkromná požiadavka HttpRequest; StringBuilder responseData = nový StringBuilder (); @Override public void channelReadComplete (ChannelHandlerContext ctx) {ctx.flush (); } @Override protected void channelRead0 (ChannelHandlerContext ctx, Object msg) {// implementácia nasledovať} @Override public void exceptionCaught (ChannelHandlerContext ctx, hodná príčina) {cause.printStackTrace (); ctx.close (); }}

Ako naznačuje názov metódy, channelReadComplete vyprázdni kontext obsluhy po spotrebovaní poslednej správy v kanáli, aby bola k dispozícii pre ďalšiu prichádzajúcu správu. Metóda výnimkaCaught slúži na riešenie prípadných výnimiek.

Zatiaľ sme videli iba štandardný kód.

Poďme teraz na zaujímavé veci, implementáciu channelRead0.

3.2. Čítanie kanálu

Náš prípad použitia je jednoduchý, server jednoducho transformuje telo žiadosti a parametre dotazu, ak existujú, na veľké písmená. Tu dávame pozor na premietnutie údajov žiadosti do odpovede - robíme to iba na demonštračné účely, aby sme pochopili, ako môžeme pomocou Netty implementovať server HTTP.

Tu, správu alebo požiadavku spotrebujeme a nastavíme jej odpoveď podľa odporúčania protokolu (poznač si to RequestUtils je niečo, čo napíšeme za chvíľu):

if (msg instanceof HttpRequest) {HttpRequest request = this.request = (HttpRequest) msg; if (HttpUtil.is100ContinueExpected (požiadavka)) {writeResponse (ctx); } responseData.setLength (0); responseData.append (RequestUtils.formatParams (požiadavka)); } responseData.append (RequestUtils.evaluateDecoderResult (požiadavka)); if (msg instanceof HttpContent) {HttpContent httpContent = (HttpContent) msg; responseData.append (RequestUtils.formatBody (httpContent)); responseData.append (RequestUtils.evaluateDecoderResult (požiadavka)); if (msg instanceof LastHttpContent) {LastHttpContent trailer = (LastHttpContent) msg; responseData.append (RequestUtils.prepareLastResponse (žiadosť, upútavka)); writeResponse (ctx, trailer, responseData); }} 

Ako vidíme, keď náš kanál dostane HttpRequest, najskôr skontroluje, či požiadavka očakáva stav 100 Pokračovať. V takom prípade okamžite odpíšeme s prázdnou odpoveďou so stavom ĎALEJ:

private void writeResponse (ChannelHandlerContext ctx) {FullHttpResponse response = new DefaultFullHttpResponse (HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER); ctx.write (odpoveď); }

Potom obslužný program inicializuje reťazec, ktorý sa má odoslať ako odpoveď, a pridá k nemu parametre dotazu žiadosti, ktoré sa majú odoslať späť tak, ako sú.

Poďme si teraz definovať metódu formatParams a umiestnite ho do a RequestUtils pomocná trieda:

StringBuilder formatParams (požiadavka HttpRequest) {StringBuilder responseData = new StringBuilder (); QueryStringDecoder queryStringDecoder = nový QueryStringDecoder (request.uri ()); Mapa params = queryStringDecoder.parameters (); if (! params.isEmpty ()) {pre (vstup p: params.entrySet ()) {Reťazcový kľúč = p.getKey (); Zoznam vals = p.getValue (); pre (String val: vals) {responseData.append ("Parameter:") .append (key.toUpperCase ()). append ("=") .append (val.toUpperCase ()). append ("\ r \ n "); }} responseData.append ("\ r \ n"); } návrat responseData; }

Ďalej po prijatí HttpContent, vezmeme telo žiadosti a prevedieme ju na veľké písmená:

StringBuilder formatBody (HttpContent httpContent) {StringBuilder responseData = nový StringBuilder (); ByteBuf content = httpContent.content (); if (content.isReadable ()) {responseData.append (content.toString (CharsetUtil.UTF_8) .toUpperCase ()) .append ("\ r \ n"); } návrat responseData; }

Tiež, ak je prijaté HttpContent je a LastHttpContent, pridáme správu na rozlúčku a prípadné koncové hlavičky:

StringBuilder prepareLastResponse (požiadavka HttpRequest, upútavka LastHttpContent) {StringBuilder responseData = new StringBuilder (); responseData.append ("Dovidenia! \ r \ n"); if (! trailer.trailingHeaders (). isEmpty ()) {responseData.append ("\ r \ n"); for (CharSequence name: trailer.trailingHeaders (). names ()) {for (CharSequence value: trailer.trailingHeaders (). getAll (name)) {responseData.append ("P.S. Trailing Header:"); responseData.append (meno) .append ("=") .append (hodnota) .append ("\ r \ n"); }} responseData.append ("\ r \ n"); } návrat responseData; }

3.3. Písanie odpovede

Teraz, keď sú naše údaje, ktoré sa majú odoslať, pripravené, môžeme odpoveď napísať do ChannelHandlerContext:

private void writeResponse (ChannelHandlerContext ctx, LastHttpContent trailer, StringBuilder responseData) {boolean keepAlive = HttpUtil.isKeepAlive (požiadavka); FullHttpResponse httpResponse = nový DefaultFullHttpResponse (HTTP_1_1, ((HttpObject) trailer) .decoderResult (). IsSuccess ()? OK: BAD_REQUEST, Unpooled.copiedBuffer (responseData.toString (), CharsetUtil )UTF) httpResponse.headers (). set (HttpHeaderNames.CONTENT_TYPE, "text / plain; charset = UTF-8"); if (keepAlive) {httpResponse.headers (). setInt (HttpHeaderNames.CONTENT_LENGTH, httpResponse.content (). readableBytes ()); httpResponse.headers (). set (HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } ctx.write (httpResponse); if (! keepAlive) {ctx.writeAndFlush (Unpooled.EMPTY_BUFFER) .addListener (ChannelFutureListener.CLOSE); }}

V tejto metóde sme vytvorili a FullHttpResponse s verziou HTTP / 1.1 a pridaním údajov, ktoré sme pripravili skôr.

Ak má byť žiadosť udržiavaná nažive, alebo inými slovami, ak sa spojenie nemá ukončiť, nastavíme odpoveď spojenie hlavička ako udržať nažive. Inak spojenie uzavrieme.

4. Testovanie servera

Aby sme otestovali náš server, pošleme niekoľko príkazov cURL a pozrime sa na odpovede.

Samozrejme, musíme server spustiť spustením triedy HttpServer pred týmto.

4.1. ZÍSKAJTE žiadosť

Najprv vyvoláme server a poskytneme cookie s požiadavkou:

zvlnenie //127.0.0.1:8080?param1=one

Ako odpoveď dostaneme:

Parameter: PARAM1 = JEDEN Zbohom! 

Môžeme aj udrieť //127.0.0.1:8080?param1=one z ktoréhokoľvek prehliadača, aby ste videli rovnaký výsledok.

4.2. POST požiadavka

Ako náš druhý test pošleme POST s telom ukážkový obsah:

curl -d "ukážkový obsah" -X POST //127.0.0.1:8080

Tu je odpoveď:

OBSAH VZORKY Ahoj!

Tentokrát, keďže naša žiadosť obsahovala telo, server ho poslal späť veľkými písmenami.

5. Záver

V tomto tutoriáli sme videli, ako implementovať protokol HTTP, najmä server HTTP používajúci Netty.

HTTP / 2 v sieti Netty demonštruje implementáciu protokolu HTTP / 2 na serveri.

Ako vždy, zdrojový kód je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found