Py学习  »  NGINX

nginx-haskell-module - 简书

Lupino • 5 年前 • 317 次点击  

nginx-haskell-module 是一个 nginx 的扩展模块,有了它我们就可以用 haskell 来写 nginx 的配置文件,甚至做一些很高级的应用。我们现在先把他的例子跑起来看看。

编译 nginx

首先下载 nginx 源码 ,然后解压:

wget https://nginx.org/download/nginx-1.15.5.tar.gz
tar xvf nginx-1.15.5.tar.gz
cd nginx-1.15.5

下载模块 echo-nginx-modulenginx-haskell-module

mkdir modules
cd modules
git clone https://github.com/openresty/echo-nginx-module.git
git clone https://github.com/lyokha/nginx-haskell-module.git
cd ..

然后编译

./configure --prefix=/root/nginx \
    --add-module=modules/nginx-haskell-module \
    --add-module=modules/echo-nginx-module \
    --with-http_ssl_module
make install

编译 Haskell 模块

使用 stack 来编译 haskell 模块

cd modules/nginx-haskell-module/haskell/ngx-export
stack init
stack build

写一个 haskell 模块

mkdir test
cd test
vim haskell.hs

内容如下:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash         #-}
{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE TupleSections     #-}
{-# LANGUAGE ViewPatterns      #-}

module NgxHaskellUserRuntime where

import           Control.Exception
import           Control.Monad
import           Data.Aeson
import qualified Data.ByteString            as B
import qualified Data.ByteString.Char8      as C8
import           Data.ByteString.Internal   (accursedUnutterablePerformIO)
import qualified Data.ByteString.Lazy       as L
import qualified Data.ByteString.Lazy.Char8 as C8L
import           Data.ByteString.Unsafe
import qualified Data.Char                  as C
import           Data.Function              (on)
import           Data.Maybe
import qualified Data.Text.Encoding         as T
import           NgxExport
import           Safe
import           Text.Pandoc
import           Text.Pandoc.Builder
import           Text.Regex.PCRE

toUpper = map C.toUpper
ngxExportSS 'toUpper

takeN = take . readDef 0
ngxExportSSS 'takeN

ngxExportSS 'reverse

class UrlDecodable a
    where doURLDecode :: a -> Maybe a

instance UrlDecodable String where
    -- adopted from
    -- http://www.rosettacode.org/wiki/URL_decoding#Haskell
    doURLDecode [] = Just []
    doURLDecode ('%' : xs) =
        case xs of
            (a : b : xss) ->
                (:) . C.chr <$> readMay ('0' : 'x' : [a, b])
                            <*> doURLDecode xss
            _ -> Nothing
    doURLDecode ('+' : xs) = (' ' :) <$> doURLDecode xs
    doURLDecode (x : xs) = (x :) <$> doURLDecode xs

instance UrlDecodable L.ByteString where
    -- adopted for ByteString arguments from
    -- http://www.rosettacode.org/wiki/URL_decoding#Haskell
    doURLDecode (L.null -> True) = Just L.empty
    doURLDecode (L.uncons -> Just (37, xs))
        | L.length xs > 1 =
            let (C8L.unpack -> c, xss) = L.splitAt 2 xs
            in L.cons <$> readMay ('0' : 'x' : c)
                      <*> doURLDecode xss
        | otherwise = Nothing
    doURLDecode (L.uncons -> Just (43, xs)) = (32 `L.cons`) <$> doURLDecode xs
    doURLDecode (L.uncons -> Just (x, xs)) = (x `L.cons`) <$> doURLDecode xs

-- does not match when any of the 2 args is empty or not decodable
matches = (fromMaybe False .) . liftM2 (=~) `on` (doURLDecode =<<) . toMaybe
    where toMaybe [] = Nothing
          toMaybe a  = Just a
ngxExportBSS 'matches

firstNotEmpty = headDef "" . filter (not . null)
ngxExportSLS 'firstNotEmpty

isInList []       = False
isInList (x : xs) = x `elem` xs
ngxExportBLS 'isInList

jSONListOfInts :: B.ByteString -> Maybe [Int]
jSONListOfInts = (decode =<<) . doURLDecode . L.fromStrict

isJSONListOfInts = isJust . jSONListOfInts
ngxExportBY 'isJSONListOfInts

jSONListOfIntsTakeN x = encode $ maybe [] (take n) $ jSONListOfInts y
    where (readDef 0 . C8.unpack -> n, B.tail -> y) = B.break (== 124) x
ngxExportYY 'jSONListOfIntsTakeN

urlDecode = fromMaybe "" . doURLDecode
ngxExportSS 'urlDecode

-- compatible with Pandoc 2.0 (will not compile for older versions)
fromMd (T.decodeUtf8 -> x) = uncurry (, packLiteral 9 "text/html"#, ) $
    case runPure $ readMarkdown def x >>= writeHtml of
        Right p -> (fromText p, 200)
        Left (displayException -> e) -> (case runPure $ writeError e of
                                             Right p -> fromText p
                                             Left  _ -> C8L.pack e, 500)
    where packLiteral l s =
              accursedUnutterablePerformIO $ unsafePackAddressLen l s
          fromText = C8L.fromStrict . T.encodeUtf8
          writeHtml = writeHtml5String defHtmlWriterOptions
          writeError = writeHtml . doc . para . singleton . Str
          defHtmlWriterOptions = def
              { writerTemplate = Just "<html>\\n<body>\\n$body$</body></html>" }
ngxExportHandler 'fromMd

toYesNo "0" = "No"
toYesNo "1" = "Yes"
toYesNo  _  = "Unknown"
ngxExportSS 'toYesNo

安装一下相关的依赖:

stack install pandoc

然后编译:

stack ghc -- -O2 -dynamic -shared -fPIC -L$(ghc --print-libdir)/rts -lHSrts_thr-ghc$(ghc --numeric-version) haskell.hs -o test.so
mkdir -p /root/nginx/modules
cp test.so /root/nginx/modules/

配置运行

vim /root/nginx/conf/nginx.conf

内容如下:

user                    nobody;
worker_processes        2;

events {
    worker_connections  1024;
}

http {
    default_type        application/octet-stream;
    sendfile            on;

    haskell load /root/nginx/modules/test.so;

    server {
        listen       8010;
        server_name  main;
        error_log    /tmp/nginx-test-haskell-error.log;
        access_log   /tmp/nginx-test-haskell-access.log;

        location / {
            haskell_run toUpper $hs_a $arg_a;
            echo "toUpper ($arg_a) = $hs_a";
            if ($arg_b) {
                haskell_run takeN $hs_a $arg_b $arg_a;
                echo "takeN ($arg_a, $arg_b) = $hs_a";
                break;
            }
            if ($arg_c) {
                haskell_run reverse $hs_a $arg_c;
                echo "reverse ($arg_c) = $hs_a";
                break;
            }
            if ($arg_d) {
                haskell_run matches $hs_a $arg_d $arg_a;
                haskell_run urlDecode $hs_b $arg_a;
                echo "matches ($arg_d, $hs_b) = $hs_a";
                break;
            }
            if ($arg_e) {
                haskell_run firstNotEmpty $hs_a $arg_f $arg_g $arg_a;
                echo "firstNotEmpty ($arg_f, $arg_g, $arg_a) = $hs_a";
                break;
            }
            if ($arg_l) {
                haskell_run isInList $hs_a $arg_a secret1 secret2 secret3;
                echo "isInList ($arg_a, <secret words>) = $hs_a";
                break;
            }
            if ($arg_m) {
                haskell_run isJSONListOfInts $hs_a $arg_m;
                haskell_run urlDecode $hs_b $arg_m;
                echo "isJSONListOfInts ($hs_b) = $hs_a";
                break;
            }
            if ($arg_n) {
                haskell_run jSONListOfIntsTakeN $hs_a $arg_take|$arg_n;
                haskell_run urlDecode $hs_b $arg_n;
                echo "jSONListOfIntsTakeN ($hs_b, $arg_take) = $hs_a";
                break;
            }
        }

        location /content {
            haskell_run isJSONListOfInts $hs_a $arg_n;
            haskell_run toYesNo $hs_b $hs_a;
            haskell_run jSONListOfIntsTakeN $hs_c $arg_take|$arg_n;
            haskell_run urlDecode $hs_d $arg_n;
            haskell_content fromMd "
## Do some JSON parsing

### Given ``$hs_d``

* Is this list of integer numbers?

    + *$hs_b*

* Take $arg_take elements

    + *``$hs_c``*
    ";

        }
    }
}

测试




    
$ curl 'http://localhost:8010/?a=hello_world'
toUpper (hello_world) = HELLO_WORLD
$ curl 'http://localhost:8010/?a=hello_world&b=4'
takeN (hello_world, 4) = hell
$ curl 'http://localhost:8010/?a=hello_world&b=oops'
takeN (hello_world, oops) = 
$ curl 'http://localhost:8010/?c=intelligence'
reverse (intelligence) = ecnegilletni
$ curl 'http://localhost:8010/?d=intelligence&a=%5Ei'              # URL-encoded ^i
matches (intelligence, ^i) = 1
$ curl 'http://localhost:8010/?d=intelligence&a=%5EI'              # URL-encoded ^I
matches (intelligence, ^I) = 0
$ curl 'http://localhost:8010/?e=1&g=intelligence&a=smart'
firstNotEmpty (, intelligence, smart) = intelligence
$ curl 'http://localhost:8010/?e=1&g=intelligence&f=smart'
firstNotEmpty (smart, intelligence, ) = smart
$ curl 'http://localhost:8010/?e=1'
firstNotEmpty (, , ) = 
$ curl 'http://localhost:8010/?l=1'
isInList (, <secret words>) = 0
$ curl 'http://localhost:8010/?l=1&a=s'
isInList (s, <secret words>) = 0
$ curl 'http://localhost:8010/?l=1&a=secret2'
isInList (secret2, <secret words>) = 1
$ curl 'http://localhost:8010/?m=%5B1%2C2%2C3%5D'                  # URL-encoded [1,2,3]
isJSONListOfInts ([1,2,3]) = 1
$ curl 'http://localhost:8010/?m=unknown'
isJSONListOfInts (unknown) = 0
$ curl 'http://localhost:8010/?n=%5B10%2C20%2C30%2C40%5D&take=3'   # URL-encoded [10,20,30,40]
jSONListOfIntsTakeN ([10,20,30,40], 3) = [10,20,30]
$ curl 'http://localhost:8010/?n=%5B10%2C20%2C30%2C40%5D&take=undefined'
jSONListOfIntsTakeN ([10,20,30,40], undefined) = []
$ curl -D- 'http://localhost:8010/content?n=%5B10%2C20%2C30%2C40%5D&take=3'
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Fri, 04 Mar 2016 15:17:44 GMT
Content-Type: text/html
Content-Length: 323
Connection: keep-alive

<html>
<body>
<h2 id="do-some-json-parsing">Do some JSON parsing</h2>
<h3 id="given-10203040">Given <code>[10,20,30,40]</code></h3>
<ul>
<li><p>Is this list of integer numbers?</p>
<ul>
<li><em>Yes</em></li>
</ul></li>
<li><p>Take 3 elements</p>
<ul>
<li><em><code>[10,20,30]</code></em></li>
</ul></li>
</ul></body></html>

结语

到这儿我们已经把模块基本的东西给跑起来了。这只是个开始,有了他我们可以做很多事情,具体可以看看 README


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/hL54aJap0U
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/24656
 
317 次点击