# Nginx

# 【錯誤處理】【Nginx】client intended to send too large body

查了一下資料發現造成的原因為 web server 接收 request body 的大小設定

- apache:  
    `LimitRequestBody` 預設為 0(unlimit)
- nginx:  
    `client_max_body_size` 預設為 1m

根據以下步驟更改一下 nginx 設定，就沒問題了

```Nginx
http {
    ...
 
    # set request body size unlimit
    client_max_body_size 0;
    ...
}
```

```
nginx -s reload
```

# 【Lua】【型別】 字串- 數字

- nil
- boolean
- number
- string
- function
- userdata
- thread
- table

## Number( 數字)

#### 字串轉數字

```Lua
tonumber("1.0")
```

#### 數字轉字串

```Lua
tostring(1.0)
```

#### 自動轉型

```Lua
-- Lua會自動轉型，如果一個字串與數字相加，會嘗試將字串轉換為數字
"1.0" + 2 -- => 3.0

-- 如果一個數字和字串使用串接，會嘗試將數字轉換成字串:
"1.0"..2 -- => 1.02
```

```Lua
-- 數字轉字串
1.0 .. ""

-- 字串轉數字
1.0 + 0 
```

---

## string (字串)

使用單引號或雙引號包起來：

```Lua
-- 單行
s1 = 'string'
s2 = "string"

-- 多行
s3 = [[
multiple
string
]]
```

#### 字串長度

```Lua
-- #
print(#"abc") -- => 3

-- string.len()
rint(string.len("abc")) -- => 3

-- 文字會有些不同
-- note: with UTF-8
print(string.len("我")) -- equal print(#"我") => 3

local utf8 = require "utf8"
print(utf8.len("你好，世界！")) -- => 6
```

####  文字串接

```Lua
-- 使用兩個點 .
print("Hello, " .. "World")  -- Hello, World

-- 多文字串接建議使用table.concat

local strs = {"Hello"," ","World"}
print(table.concat(strs,""))  -- Hello, World
```

```Lua
-- format
local string = require "string"
print(string.format("%s !! %s","Hello","World")) -- > Hello !! World

```

---

`file`的資料型態--`userdata`

```Lua
do
  local f <close> = io.open("text.txt",'w')
  print(type(f)) -- Output: userdata
end
```

# 【Lua】【型別】 函數function

#### 函數宣告

函數可以使用`function`來做宣告，並以`end`結束

```Lua
function hello()
  print("Hello, World")
end

-- 等同以下
hello = function()
  print("Hello, World")
end
```

```Lua
function genFun()
  return function()
    print("Hello, World")
  end
end


function callFun(f)
  f()
end


local _f = genFun() -- # get a function
callFun(_f) -- print("Hello, World")
```

#### 函數傳遞

```Lua
function plus(a, b)
    print(a,b, a + b)
    return a+b ,a ,b
end

local anser, A, B = plus(1,2)
print(A.."+"..B.."="..anser)

-- 1	2	3
-- 1+2=3
```

#### 回傳值規則

若返回值個數大於接收變量的個數，多餘的返回值會被忽略掉； 若返回值個數小 於參數個數，從左向右，沒有被返回值初始化的變量會被初始化為 nil。

```Lua
function init() --init 函数 返回两个值 1 和 "lua"
return 1, "lua"
end
x = init()
print(x)
x, y, z = init()
print(x, y, z)

--output
1
1 lua nil
```

当一个函数有一个以上返回值，且函数调用不是一个列表表达式的<span style="background-color: #f1c40f;">最后一个元素</span>，那么函数 调用只会产生一个返回值, 也就是第一个返回值。

```Lua
local function init() -- init 函数 返回两个值 1 和 "lua"
return 1, "lua"
end
local x, y, z = init(), 2 -- init 函数的位置不在最后，此时只返回 1
print(x, y, z) -->output 1 2 nil
local a, b, c = 2, init() -- init 函数的位置在最后，此时返回 1 和 "lua"
print(a, b, c) -->output 2 1 
```

####  

```Lua
local function init() -- init 函数 返回两个值 1 和 "lua"
return 1, "lua"
end
local x, y, z = init(), 2 -- init 函数的位置不在最后，此时只返回 1
print(x, y, z) -->output 1 2 nil
local a, b, c = 2, init() -- init 函数的位置在最后，此时返回 1 和 "lua"
print(a, b, c) -->output 2 1 
```

#### 不定長參數

```Lua
function hello(...)
  local members = {...}
  print("有多少成員: ", #members)
  for _,m in pairs(members) do
    print("Hello, "..m)
  end
end

-- hello("Bob", "Lua", "Luna", "Selene")
-- 有多少成員: 	4
-- Hello, Bob
-- Hello, Lua
-- Hello, Luna
-- Hello, Selene

```

```Lua
function mylog(...)
	local arr = {...}
	table.insert(arr,1,"\n=====[mylog_begin]=====")
	table.insert(arr,"=====[mylog_end]=====\n")
	local print_str = table.concat(arr,"\n")
	-- for key,value in pairs(arr) do
	-- 	-- ngx.say(key..":"..value)
	-- 	ngx.say(value)
	-- end	
	ngx.log(ngx.ERR,print_str)
end


mylog("開始",1,2,3,"結束")
-- =====[mylog_begin]=====
-- 開始
-- 1
-- 2
-- 3
-- 結束
-- =====[mylog_end]=====
```

#### 立即呼叫函式表達式(IIFE) 與 匿名函式

```Lua
(function ()
  print("Hello, World")
end)()
```

---

#### 多值返回

```Lua
function get(dict, key, default)
  return (dict[key] or default), dict[key] ~= nil
end

_d = {}
_v, found = get(_d, "key", "value")
print(_v) -- => Output: value
print(found) -- => Output: false
```

---

```Lua
function Person(args)
  local person = args
  person.name = args.name or "Bob"
  person.age = args.age or 20
  person.address = args.address or ""
  return person
end

p1 = Person {
  "位置參數1",
  age = 13,
  address = "secret",
  "位置參數2",
}

print("Name:    ", p1.name)
print("Age:     ", p1.age)
print("Address: ", p1.address)
print("-------------")
print("位置參數：", p1[1], p1[2])
```

```
Name: Bob
Age: 13
Address: secret

位置參數： 位置參數1 位置參數2
```

# 常用的內建函數

## 基本

- ipairs
- pairs
- print
- require
- tonumber
- tostring
- type

## 輸入/輸出

估計本系列不怎麼會提到，先放這XD。

- io.close
- io.input
- io.lines
- io.open
- io.output
- io.popen
- io.read
- io.stderr
- io.stdin
- io.stdout
- io.tmpfile
- io.type
- io.write
- file:close
- file:flush
- file:lines
- file:read
- file:seek
- file:write

## 數學

- math.abs
- math.ceil
- math.exp
- math.floor
- math.huge (無限大)
- math.log
- math.max
- math.maxinteger
- math.min
- math.mininteger
- math.pi (PI 常數)
- math.random
- math.randomseed
- math.tointeger
- math.type

## 作業系統

- os.clock
- os.date
- os.difftime
- os.execute
- os.exit
- os.getenv
- os.time

> Lua 5.1前一個和`os.getenv`很像的函數`setfenv`其實也蠻常用的，後來被移除，改用其他方式。  
> 當前還有很多是使用Lua 5.1，這雖然是需要知道的事情，不過其實其與作業系統無關。

## 字串

- string.byte
- string.char
- string.find
- string.format
- string.len
- string.reverse
- string.sub
- utf8.char
- utf8.codes
- utf8.len

# 【Lua】【型別】 nil - 布林

## nil

`nil`是Lua裡的一個特殊值，**代表什麼也沒有**。其型別也是`nil`

```Lua
type(nil) -- => nil
```

## 布林

#### <span class="s1">布林值只有</span><span style="background-color: #ffff99;">true</span><span class="s1">和</span><span style="background-color: #ffff99;">false</span>

#### 只要不是<span style="background-color: #ffff00;"><span style="font-family: Lucida Console, DejaVu Sans Mono, Ubuntu Mono, Monaco, monospace;"><span style="font-size: 19.5922px; white-space: pre-wrap; background-color: #ffff00;">nil</span></span></span>或是<span style="background-color: #ffff00;"><span style="font-family: Lucida Console, DejaVu Sans Mono, Ubuntu Mono, Monaco, monospace;"><span style="font-size: 19.5922px; white-space: pre-wrap; background-color: #ffff00;">false</span></span></span>都為真， **包含0、空表、空字串** 。

```Lua
if 0 then
  print("0 is true")
end

if {} then
  print("{} is true")
end

if "" then
  print [["" is true]]
end
```

#### ~= 不等於

```Lua
weeks = {"週一","週二","週三","週四","週五","週六","週日", "Opps"}

if #weeks ~= 7 then
  error("Weeks length must is 7.")
end
```

# 【Lua】【流程控制】for

```Lua
-- for i = {起始值}, {結束值}, {step] do
for i = 1, 10, 2 do
  print(i)
end


-- 1
-- 3
-- 5
-- 7
-- 9
```

迭代

```Lua
array = {"one", "two", "three"}

for i, v in ipairs(array) do
  print(i, v)
end

-- 1	one
-- 2	two
-- 3	three

```

```Lua
array = {"one", "two", "three"}
 
for i in ipairs(array) do
  print(i, array[i])
end

-- 1	one
-- 2	two
-- 3	three
```

```Lua
tb = {
  ["key1"] = "one",
  ["key2"] = "two",
  ["key3"] = "three",
}

for k, v in pairs(tb) do
  print(k, v)
end

-- key3	three
-- key1	one
-- key2	two
```

# 【Lua】【流程控制】if

####  

```Lua
if true then
  print("if block")
elseif true then
  print("elseif block")
else
  print("else")
end
```

```Lua
if not true then
  print("not true")
else
  print("else")
end
```

#### <span style="color: #ff0000;">elseif</span> 中間<span style="background-color: #ffff00;">沒有空白</span>要注意

```Lua
if true then
  print("if block")
else if true then
  print("elseif block")
else
  print("else")
end
end

-- 其實等於

if true then
    print("if block")
else 
    if true then
        print("elseif block")
    else
        print("else")
    end
end

```

# 【Lua】【流程控制】while

```Lua
destination = 5  -- 終點位置
current_pos = 0  -- 目前位置

print([[馬拉松賽跑
============]])

print("終點位於："..destination)
print("目前在："..current_pos)

while current_pos < destination do
  current_pos = current_pos + 1
  print("往前跑1㎞，目前位於"..current_pos)
end

print("抵達終點，目前位於"..current_pos)
```

```
馬拉松賽跑
============
終點位於：5
目前在：0
往前跑1㎞，目前位於1
往前跑1㎞，目前位於2
往前跑1㎞，目前位於3
往前跑1㎞，目前位於4
往前跑1㎞，目前位於5
抵達終點，目前位於5


```

# 【Lua】【優化】請求返回後繼續執行

ngx.eof 關閉連線，data 返還user，後續代碼繼續進行

```Lua
local response, user_stat = logic_func.get_response(request)
ngx.say(response)
ngx.eof()
if user_stat then
local ret = db_redis.update_user_data(user_stat)
end
```

# 【Lua】安裝 luarocks

[https://github.com/luarocks/luarocks/wiki/Download](https://github.com/luarocks/luarocks/wiki/Download)

[https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Unix](https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Unix)

```shell
apt-get update
apt install build-essential libreadline-dev unzip wget zlib1g-dev -y

#安裝 luarocks
wget https://luarocks.org/releases/luarocks-3.8.0.tar.gz && tar zxpf luarocks-3.8.0.tar.gz && cd luarocks-3.8.0 
./configure 
make && make install

#安裝 lua-resty-http
luarocks install lua-resty-http
#安裝 lua-ffi-zlib
luarocks install lua-ffi-zlib
```

### install lua-zlib

<div id="bkmrk-https%3A%2F%2Fblog.51cto.c">[https://blog.51cto.com/u\_5650011/5394910](https://blog.51cto.com/u_5650011/5394910)</div><div id="bkmrk-"></div>```
cd /usr/local
wget https://github.com/brimworks/lua-zlib/archive/master.zip
unzip lua-zlib-master.zip
```

```
cd /usr/local/lua-zlib-master
cmake -DLUA_INCLUDE_DIR=/usr/local/openresty/luajit/include/luajit-2.1 -DLUA_LIBRARIES=/usr/local/openresty/luajit/lib -DUSE_LUAJIT=ON -DUSE_LUA=OFF
```

```
make
cp zlib.so /usr/local/openresty/lualib/zlib.so
```

# 【Lua】註解

### 單行註解

```Lua
 --  這是註解
```

### 多行註解

```Lua
-- 使用 --[[ 內文 ]]-- 包起來

--[[
 function log_header_body()
        -- 取出列表編號
        local t = cjson.decode(ngx.req.get_body_data())
        local body =  cjson.encode(t)
        ngx.log(ngx.ERR, body)
        local headers = cjson.encode(ngx.req.get_headers())
        ngx.log(ngx.ERR, headers)
    
end
]]--
```

#### 特數多行註解

可在--\[ ${這中間插入任意數量=}\[ ..... \] ${這中間插入任意數量=}\]--

```Lua
--[===[
Some Comments
==============
特別插入--]]  但實際註釋還未結束
--]===] print("這段不是註釋") -- end comment
```

# 【Lua】load

與 javascipt eval 相同－動態產生並執行程式碼

```Lua
-- load不回直接執行，其實其返回一個包裝函式：

f = load[[g2 = 2]]
print(type(f)) -- => function

print(g2) -- => Output: nil
f()
print(g2) -- => Output: 2
```

# 【Lua】ngx_lua_module

[![image-1676368742762.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-02/scaled-1680-/4ZBk8mEgtc6Hwo7h-image-1676368742762.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-02/4ZBk8mEgtc6Hwo7h-image-1676368742762.png)

<table class="full-width fixed-word-wrap" id="bkmrk-%E6%8C%87%E4%BB%A4-%E8%AA%AA%E6%98%8E-set_by_lua%2A-%E6%B5%81%E7%A8%8B" style="height: 232px;"><thead><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">指令</td><td style="height: 29px; width: 423px;">說明</td></tr></thead><tbody><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">set\_by\_lua\*</td><td style="height: 29px; width: 423px;">流程分支處理判斷變量初始化</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">rewrite\_by\_lua\*</td><td style="height: 29px; width: 423px;">轉發、重定向、緩存等功能</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">access\_by\_lua\*</td><td style="height: 29px; width: 423px;">IP 准入、身份驗證、接口權限、解密</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">content\_by\_lua\*</td><td style="height: 29px; width: 423px;">內容生成</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">header\_filter\_by\_lua\*</td><td style="height: 29px; width: 423px;">響應頭部過濾處理，可以添加響應頭</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">body\_filter\_by\_lua\*</td><td style="height: 29px; width: 423px;">響應體過濾處理，例如轉換響應體</td></tr><tr style="height: 29px;"><td style="height: 29px; width: 154.539px;">log\_by\_lua\*</td><td style="height: 29px; width: 423px;">異步完成日誌記錄，日誌可以記錄在本地，還可以同步到其他機器</td></tr></tbody></table>

儘管僅使用單個階段的指令content\_by\_lua\*就可以完成以上職責，但是把邏輯劃分在不同階段，更加容易維護。

[https://blog.gmem.cc/openresty-study-note](https://blog.gmem.cc/openresty-study-note)

[https://github.com/openresty/lua-nginx-module](https://github.com/openresty/lua-nginx-module)

# 【Lua】Nginx 變量

<div id="bkmrk-%E4%BD%BF%E7%94%A8ng%E8%AE%8A%E9%87%8F"><span class="graybg"><span>使用Ng變量</span></span></div><span>要在OpenResty中引用Nginx變量，可以使用 </span><span class="crayon-syntax crayon-syntax-inline  crayon-theme-gmem-github crayon-theme-gmem-github-inline crayon-font-consolas" id="bkmrk-ngx%C2%A0.%C2%A0var%C2%A0.%C2%A0variable"><span class="crayon-pre crayon-code"><span class="crayon-v"><span>ngx </span></span><span class="crayon-sy"><span>. </span></span><span class="crayon-t"><span>var </span></span><span class="crayon-sy"><span>. </span></span><span class="crayon-v"><span>VARIABLE</span></span></span></span><span>，要將變量從字符串轉換為數字，可以使用 </span><span class="crayon-syntax crayon-syntax-inline  crayon-theme-gmem-github crayon-theme-gmem-github-inline crayon-font-consolas" id="bkmrk-tonumber"><span class="crayon-pre crayon-code"><span class="crayon-v"><span>tonumber</span></span></span></span><span>函數。</span>

```Lua
-- 黑名单
local black_ips = {["127.0.0.1"]=true}
 
-- 当前客户端IP
local ip = ngx.var.remote_addr
if true == black_ips[ip] then
    -- 返回相应的HTTP状态码
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

<span>經常用到的Ng變量如下表：</span>

<table class="full-width fixed-word-wrap" id="bkmrk-%E8%AE%8A%E9%87%8F-%E8%AA%AA%E6%98%8E-arg_name-%E8%AB%8B%E6%B1%82%E4%B8%AD%E7%9A%84n"><thead><tr><td><span>變量</span></td><td><span>說明</span></td></tr></thead><tbody><tr><td><span>arg\_name</span></td><td><span>請求中的name參數</span></td></tr><tr><td><span>args</span></td><td><span>請求中的參數</span></td></tr><tr><td><span>binary\_remote\_addr</span></td><td><span>遠程地址的二進製表示</span></td></tr><tr><td><span>body\_bytes\_sent</span></td><td><span>已發送的消息體字節數</span></td></tr><tr><td><span>content\_length</span></td><td><span>HTTP請求信息裡的"Content-Length"</span></td></tr><tr><td><span>content\_type</span></td><td><span>請求信息裡的"Content-Type"</span></td></tr><tr><td><span>document\_root</span></td><td><span>針對當前請求的根路徑設置值</span></td></tr><tr><td><span>document\_uri</span></td><td><span>與$uri相同; 比如/test2/test.php</span></td></tr><tr><td><span>host</span></td><td><span>請求信息中的"Host"，如果請求中沒有Host行，則等於設置的服務器名</span></td></tr><tr><td><span>hostname</span></td><td><span>機器名使用gethostname系統調用的值</span></td></tr><tr><td><span>http\_cookie</span></td><td><span>Cookie信息</span></td></tr><tr><td><span>http\_referer</span></td><td><span>引用地址</span></td></tr><tr><td><span>http\_user\_agent</span></td><td><span>客戶端代理信息</span></td></tr><tr><td><span>http\_via</span></td><td><span>最後一個訪問服務器的Ip地址。</span></td></tr><tr><td><span>http\_x\_forwarded\_for</span></td><td><span>相當於網絡訪問路徑</span></td></tr><tr><td><span>is\_args</span></td><td><span>如果請求行帶有參數，返回“?”，否則返回空字符串</span></td></tr><tr><td><span>limit\_rate</span></td><td><span>對連接速率的限制。此變量支持寫入：</span>

<div data-settings=" no-popup minimize scroll-mouseover"><div data-settings=" show"><div><div><div></div></div><div><div></div></div><div><div></div></div><span class="crayon-language">Lua</span></div></div><div></div><div></div><div><table class="crayon-table"><tbody><tr class="crayon-row"><td class="crayon-nums " data-settings="show"><div><div data-line="crayon-63eb517b82d78653021122-1">1</div><div data-line="crayon-63eb517b82d78653021122-2">2</div></div></td><td class="crayon-code"><div><div><span class="crayon-c">-- 设置当前请求的响应传递速率限制</span></div><div><span class="crayon-v">ngx</span><span class="crayon-sy">.</span><span class="crayon-v">var</span><span class="crayon-sy">.</span><span class="crayon-v">limit\_rate</span><span class="crayon-h"> </span><span class="crayon-o">=</span><span class="crayon-h"> </span><span class="crayon-cn">1000</span> </div></div></td></tr></tbody></table>

</div></div></td></tr><tr><td><span>nginx\_version</span></td><td><span>當前運行的nginx版本號</span></td></tr><tr><td><span>pid</span></td><td><span>Worker進程的PID</span></td></tr><tr><td><span>query\_string</span></td><td><span>與$args相同</span></td></tr><tr><td><span>realpath\_root</span></td><td><span>按root指令或alias指令算出的當前請求的絕對路徑。其中的符號鏈接都會解析成真是文件路徑</span></td></tr><tr><td><span>remote\_addr</span></td><td><span>客戶端IP地址</span></td></tr><tr><td><span>remote\_port</span></td><td><span>客戶端端口號</span></td></tr><tr><td><span>remote\_user</span></td><td><span>客戶端用戶名，認證用</span></td></tr><tr><td><span>request</span></td><td><span>用戶請求</span></td></tr><tr><td><span>request\_body</span></td><td><span>這個變量（0.7.58+）包含請求的主要信息。在使用proxy\_pass或fastcgi\_pass指令的location中比較有意義</span></td></tr><tr><td><span>request\_body\_file</span></td><td><span>客戶端請求主體信息的臨時文件名</span></td></tr><tr><td><span>request\_completion</span></td><td><span>如果請求成功，設為"OK"；如果請求未完成或者不是一系列請求中最後一部分則設為空</span></td></tr><tr><td><span>request\_filename</span></td><td><span>當前請求的文件路徑名，比如/opt/nginx/www/test.php</span></td></tr><tr><td><span>request\_method</span></td><td><span>請求的方法，比如"GET"、"POST"等</span></td></tr><tr><td><span>request\_uri</span></td><td><span>請求的URI，帶參數</span></td></tr><tr><td><span>scheme</span></td><td><span>所用的協議，比如http或者是https</span></td></tr><tr><td><span>server\_addr</span></td><td><span>服務器地址，如果沒有用listen指明服務器地址，使用這個變量將發起一次系統調用以取得地址(造成資源浪費)</span></td></tr><tr><td><span>server\_name</span></td><td><span>請求到達的服務器名</span></td></tr><tr><td><span>server\_port</span></td><td><span>請求到達的服務器端口號</span></td></tr><tr><td><span>server\_protocol</span></td><td><span>請求的協議版本，"HTTP/1.0"或"HTTP/1.1"</span></td></tr><tr><td><span>uri</span></td><td><span>請求的URI，可能和最初的值有不同，比如經過重定向之類的</span></td></tr></tbody></table>

# 【Lua】ngx.say 與 ngx.print 差異

差異在 ngx.say 會加入一個換行符號

# 【Lua】SSL相關指令執行順序

<div drawio-diagram="522"><img src="https://bookstack.treemanou.com/uploads/images/drawio/2023-02/dyJfouYkGTnWwOup-drawing-3-1676368425.png" alt=""/></div>

# 【Lua】table

### 模擬陣列用法

#### <span style="color: #ff0000;">注意</span> lua 索引 從<span style="background-color: #ffff00;"> 1</span> 開始

```Lua
-- 宣告 table
lang = {
  "C",
  "C#",
  "C++",
  "Java",
  "Swift",
  "Python",
  "Haskell",
}

-- 使用 ipairs 迭代 lang
for i in ipairs(lang) do
    print(i, #lang[i], lang[i])
end

-- 以下寫法相同
for i,v in ipairs(lang) do
    print(i, #v, v)
end


-- 1	1	C
-- 2	2	C#
-- 3	3	C++
-- 4	4	Java
-- 5	5	Swift
-- 6	6	Python
-- 7	7	Haskell
```

###  新增資料 =&gt; table.insert

```Lua
lang = {
  "C",
  "C#",
  "C++",
  "Java",
  "Swift",
  "Python",
  "Haskell",
}

-- 使用table.insert
table.insert(lang, "lua")
-- 使用索引
lang[9] = "Javascript"

-- 1	1	C
-- 2	2	C#
-- 3	3	C++
-- 4	4	Java
-- 5	5	Swift
-- 6	6	Python
-- 7	7	Haskell
-- 8	3	lua
-- 9	10	Javascript
```

### 刪除資料 =&gt; 設為 nil

```Lua
lang = {
  "C",
  "C#",
  "C++",
  "Java",
  "Swift",
  "Python",
  "Haskell",
}

-- 刪除總是山最後一筆，不然會有意外錯誤
lang[8] = nil

for i,v in ipairs(lang) do
    print(i, #v, v)
end
-- 1	1	C
-- 2	2	C#
-- 3	3	C++
-- 4	4	Java
-- 5	5	Swift
-- 6	6	Python
-- 7	7	Haskell
```

### 類map(key,vlaue)宣告

#### key 不要<span style="color: #ff0000;">文字</span>,<span style="color: #ff0000;">數字</span>混和宣告

```Lua
person = {
  ["name"] = "Bob",
  ["age"] = 25,
}

-- key不為數字－以上相同
person = {
  name = "Bob",
  age = 25,
}

-- 陣列用法
arr = {
  [1] = 1,
  [2] = 2,
  [3] = 3,
  [4] = 4,
}
```

### Key 值範圍

key可以是除了`nil`和`NaN`(Not a Number)以外的任何型別。

```Lua
obj = {} -- 建立一個空表
obj[1] = 1 -- 整數是合法的key值
obj[1.0] = 2 -- 浮點數是合法的key值
obj["string"] = 1 -- 字串是合法的key值
obj[math.huge] = 1 -- inf是合法的key值

--[[ 
要注意的是 obj[1] 和 obj[1.0]相同
其obj[1]和obj[1.0]最終值為2
--]]

----------------

print(obj[nil]) --> nil
obj[nil] = 1 --> Error: nil不是合法的key值，儘管取值不會出錯

print(obj[0/0]) --> nil
print(0/0) --> -nan
obj[0/0] = 1 --> Error: NaN不是合法的key值
```

# 【Lua】vscode 外掛

[https://marketplace.visualstudio.com/items?itemName=sumneko.lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua)

[![image-1677036811172.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-02/scaled-1680-/c9fZHREKy1LWZx5U-image-1677036811172.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-02/c9fZHREKy1LWZx5U-image-1677036811172.png)

# 【Nginx】config 詳解

#### 基本配置區塊

```Nginx
#config 區塊 基本配置
...                   # 全域性區塊
 
event{                # events 區塊
        ...
}
http{                 # http 區塊
    server{           # server 區塊
        location{     # location 區塊
            ...
        }
    }
}
```

#### server 區塊的配置

```Nginx
server{
      listen  80;
      listen [::]80;
      server_name  example.com  example2.com; 
    location /{
        root /usr/share/nginx/html;
    	index index.html index.htm;
    }
}

#listen 80; 代表監聽所有 ipv4 的位址
#listen [::]80; 代表監聽所有 ipv6 的位址
#server_name 是你的 Domain 名稱。 可以使用空白listen 多個domain
#location 中則是指定對不同路徑要怎麼處理
```

```Nginx
# 如果沒有下deault_server; 設定，第一個server 區塊會變成預設所以有位被匹配到的設定
# 避免除錯上的困難，可設定一個預設區塊，在未匹配到server 時，顯示403錯誤
server {
  listen 80 default_server;
  server_name _;

  set $cache_status -;
  set $parameter -;
  set $response_body_size 0;
  location / {
  default_type 'text/html';
  echo "default server" ;
  return 403; # 403 forbidden
  }
}
	
```

#### 【反向代理】reverse proxy

```Nginx
server {
 
    listen 80;
    server_name www.example.com;
 
    location \ {  
           proxy_pass http://192.168.1.1:8080;  
           
           proxy_set_header Host 				$host;
           proxy_set_header X-Real-IP			$remote_addr;
           proxy_set_header X-Forwarded-For		$proxy_add_x_forwarded_for;
           proxy_set_header X-Forwarded-Proto	$scheme;
        }
}
```

####  【快取】實作圖片快取機制

```Nginx
server {
 
    listen 80;
    server_name www.example.com;
 
    location ~* \.(jpg|jpeg|png|css|js)$ {  
            expires 365d;  # 快取時間
            proxy_pass http://loaclhost:8080;  
        }
}
```

#### 【virtural host】實作 virtural host

```Nginx
# .web1
server {
    listen      80;
    server_name web1.example.com ;
    root /usr/loacl/nginx/web1;
    index index.html;
}
 
# .web2
server {
    listen      80;
    server_name web2.example.com ;
    root /usr/loacl/nginx/web2;
    index index.html;
}
 
```

#### 【負載平衡】 load balance

```Nginx
#Nginx 提供了以下三種 load balancing 方法：

#round-robin：預設值，會將請留輪流平均分配到每台伺服器上
#lest-connected：會將請求分配到目前連接數最少的伺服器上
#ip-hash：利用 hash-function 來決定使用者要被分配到的伺服器，此方法可以達到同一個使用者 (IP address) 每次連結的伺服器都是相同的
#如果要從默認值 round-robin 方法改成 lest-connected 或 ip-hash 的方法，只需要在第一行加上lest_conn; 或 ip_hash; 即可
upstream myapp {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}
server {
  listen 80;
  location / {
    proxy_pass   http://myapp;
  }
}
```

```Nginx
# weight 默認值為 1，
# 以下的配置代表如果有 5 次新的請求，則會有 3 次被分配到 srv1 和分配各 1 次到 srv2 srv3 上
upstream myapp1 {
    server srv1.example.com weight=3;
    server srv2.example.com;
    server srv3.example.com;
    server bak.example.com; #backup 代表，所有伺服器都掛掉之後，此伺服器才會生效
}
server {
  listen 80;
  location / {
    proxy_pass   http://myapp1;
  }
}
```

---

#### 限制來源

```
location / {
    deny  192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    allow 2001:0db8::/32;
    deny  all;
}
```

#### nginx.conf 詳細配置

```Nginx
#定义Nginx运行的用户和用户组
user www www;


#nginx進程數，建議設置為等於CPU總核心數。
worker_processes 8;


#全局錯誤日誌定義類型，[ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log info;


#進程文件
pid /var/run/nginx.pid;


#一個nginx進程打開的最多文件描述符數目，理論值應該是最多打開文件數（系統的值ulimit -n）與nginx進程數相除，但是nginx分配請求並不均勻，所以建議與ulimit -n的值保持一致。
worker_rlimit_nofile 65535;


#工作模式與連接數上限
events
{
#參考事件模型，use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本內核中的高性能網絡I/O模型，如果跑在FreeBSD上面，就用kqueue模型。
use epoll;
#單個進程最大連接數（最大連接數=連接數*進程數）
worker_connections 65535;
}

#設定http服務器
http
{
include mime.types; #文件擴展名與文件類型映射表
default_type application/octet-stream; #默認文件類型
#charset utf-8; #默認編碼
server_names_hash_bucket_size 128; #服務器名字的hash表大小
large_client_header_buffers 4 64k; #設定請求緩
autoindex on; #開啟目錄列表訪問，合適下載服務器，默認關閉。

#此指令設置 Nginx 使用的 DNS 解析器的配置。在這個例子中，使用了兩個 DNS 解析器的 IP 地址：8.8.8.8 和 168.95.1.1。
#ipv6=off 表示禁用 IPv6 解析。valid=300s 表示 DNS 解析的結果將在 300 秒（5 分鐘）內被緩存。
resolver 8.8.8.8 168.95.1.1 ipv6=off valid=300s;
  	
#此指令設置 DNS 解析的超時時間。如果在指定的時間內無法解析域名，Nginx 將中斷解析並返回錯誤。
resolver_timeout 3s;
  
client_body_buffer_size 256K; 	#此指令指定客戶端請求主體的緩衝區大小，這是用於存儲客戶端發送的請求主體數據的內存區域。
client_body_timeout 3s; 		#此指令設置接收客戶端請求主體的超時時間。如果在指定的時間內沒有接收到完整的請求主體，Nginx 將結束該連接。
client_header_buffer_size 64k;	#此指令指定用於存儲客戶端請求標頭的緩衝區大小。(?上傳文件大小限制?)
client_header_timeout 3s;		#此指令設置接收客戶端請求標頭的超時時間。如果在指定的時間內沒有接收到完整的請求標頭，Nginx 將結束該連接。
client_max_body_size 1m;		#此指令設置允許客戶端發送的最大請求主體大小。如果客戶端嘗試發送超過此大小的請求主體，Nginx 將返回 413 Request Entity Too Large 錯誤。
large_client_header_buffers 8 64k; #此指令設置用於存儲大型客戶端請求標頭的緩衝區大小。如果客戶端請求的標頭超過指定的大小，Nginx 將使用這些緩衝區來處理它。
keepalive_timeout 5s;			#此指令設置持久連接的超時時間。如果在指定的時間內沒有新的請求到達，Nginx 將關閉持久連接。
open_file_cache max=5000 inactive=20s; #此指令啟用了文件打開時的緩存機制，以加快文件的讀取速度。max 參數指定緩存的最大數量，inactive 參數指定文件在緩存中保持不活躍的時間。
open_file_cache_valid 30s;		#此指令設置緩存中的文件信息的有效期限。在此期限內，Nginx 將重用緩存的文件信息而不需要再次訪問磁盤。
open_file_cache_min_uses 8;		#此指令設置文件緩存機制中的最小使用次數。只有當某個文件被訪問的次數超過此設定值時，才會將該文件的信息緩存起來。
open_file_cache_errors on;		#此指令設置是否緩存文件打開期間的錯誤。當設置為 "on" 時，如果在打開文件時發生錯誤，Nginx 會將該錯誤信息緩存起來，以避免重複出現相同的錯誤。
send_timeout 10;				#此指令設置向客戶端發送數據的超時時間。如果在指定的時間內沒有將數據發送給客戶端，Nginx 將結束該連接。

#開啟高效文件傳輸模式，sendfile指令指定nginx是否調用sendfile函數來輸出文件，對於普通應用設為on，用於高效地將文件內容發送給客戶端，而無需將文件數據從磁盤讀入到用戶空間。
#如果用來進行下載等應用磁盤IO重負載應用，可設置為off，以平衡磁盤與網絡I/O處理速度，降低系統的負載。注意：如果圖片顯示不正常把這個改成off。
sendfile on;
  
sendfile_max_chunk 512k;		#此指令設置單次 sendfile 操作的最大數據塊大小。如果需要發送的文件數據大於此值，Nginx 將分段進行 sendfile 操作。
server_tokens off;				#此指令關閉 Nginx 的版本號顯示。在生產環境中，關閉版本號的顯示可以增加安全性，防止攻擊者利用已知漏洞進行攻擊。
tcp_nodelay on;					#此指令啟用 TCP 即時發送功能。它禁用了 Nagle 算法，從而降低了數據傳輸的延遲，但可能會增加網絡流量。
tcp_nopush  on;					#此指令啟用 TCP 壓縮機制。當服務器將數據發送給客戶端時，它將盡可能地將多個小的數據包合並為一個較大的數據包發送，從而減少了網絡傳輸的開銷。
types_hash_max_size 2048;		#此指令設置 Nginx 在處理 MIME 類型時使用的哈希表的大小。增大此值可以提高性能

#FastCGI相關參數是為了改善網站的性能：減少資源佔用，提高訪問速度。下面參數看字面意思都能理解。
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;


#gzip模塊設置
gzip on; #開啟gzip壓縮輸出
gzip_min_length 1k; #最小壓縮文件大小
gzip_buffers 4 16k; #壓縮緩衝區
gzip_http_version 1.0; #壓縮版本（默認1.1，前端如果是squid2.5請使用1.0）
gzip_comp_level 2; #壓縮等級
gzip_types text/plain application/x-javascript text/css application/xml;
#壓縮類型，默認就已經包含text/html，所以下面就不用再寫了，寫上去也不會有問題，但是會有一個warn。
gzip_vary on;
#limit_zone crawler $binary_remote_addr 10m; #開啟限制IP連接數的時候需要使用


upstream blog.ha97.com {
#upstream的負載均衡，weight是權重，可以根據機器配置定義權重。weigth參數表示權值，權值越高被分配到的機率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}


#虛擬主機的配置
server
{
#監聽端口
listen 80;
#域名可以有多個，用空格隔開
server_name www.ha97.com ha97.com;
index index.html index.htm index.php;
root /data/www/ha97;
location ~ .*\.(php|php5)?$
{
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
#圖片緩存時間設置
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 10d;
}
#JS和CSS緩存時間設置
location ~ .*\.(js|css)?$
{
expires 1h;
}
#日誌格式設定
log_format access '$remote_addr – $remote_user [$time_local] “$request” '
'$status $body_bytes_sent “$http_referer” '
'”$http_user_agent” $http_x_forwarded_for';
#定義本虛擬主機的訪問日誌
access_log /var/log/nginx/ha97access.log access;


#對“/” 啟用反向代理
location / {
proxy_pass http://127.0.0.1:88;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
#後端的Web服務器可以通過X-Forwarded-For獲取用戶真實IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#以下是一些反向代理的配置，可選。
proxy_set_header Host $host;
client_max_body_size 10m; #允許客戶端請求的最大單文件字節數
client_body_buffer_size 128k; #緩衝區代理緩衝用戶端請求的最大字節數，
proxy_connect_timeout 90; #nginx跟後端服務器連接超時時間(代理連接超時)
proxy_send_timeout 90; #後端服務器數據回傳時間(代理髮送超時)
proxy_read_timeout 90; #連接成功後，後端服務器響應時間(代理接收超時)
proxy_buffer_size 4k; #設置代理服務器（nginx）保存用戶頭信息的緩衝區大小
proxy_buffers 4 32k; #proxy_buffers緩衝區，網頁平均在32k以下的設置
proxy_busy_buffers_size 64k; #高負荷下緩衝大小（proxy_buffers*2）
proxy_temp_file_write_size 64k;
#設定緩存文件夾大小，大於這個值，將從upstream服務器傳
}


#設定查看Nginx狀態的地址
location /NginxStatus {
stub_status on;
access_log on;
auth_basic “NginxStatus”;
auth_basic_user_file conf/htpasswd;
#htpasswd文件的內容可以用apache提供的htpasswd工具來產生。
}



#本地动静分离反向代理配置
#所有jsp的页面均交由tomcat或resin处理
location ~ .(jsp|jspx|do)?$ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080;
}
#所有静态文件由nginx直接读取不经过tomcat或resin
location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$
{ expires 15d; }
location ~ .*.(js|css)?$
{ expires 1h; }
}
}
```

# 【Nginx】location 匹配規則

### 一般匹配

location \[=|~|~\*|^~\] /uri/ { ... }

<table id="bkmrk-%E6%A8%A1%E5%BC%8F-%E5%90%AB%E4%B9%89-location-%3D-%2Fur"><thead><tr><th>模式</th><th>含义</th></tr></thead><tbody><tr><td>location = /uri</td><td>= 表示精确匹配，只有完全匹配上才能生效</td></tr><tr><td>location ^~ /uri</td><td>^~ 开头对URL路径进行前缀匹配，并且在正则之前。</td></tr><tr><td>location ~ pattern</td><td>开头表示区分大小写的正则匹配</td></tr><tr><td>location ~\* pattern</td><td>开头表示不区分大小写的正则匹配</td></tr><tr><td>location /uri</td><td>不带任何修饰符，也表示前缀匹配，但是在正则匹配之后</td></tr><tr><td>location /</td><td>通用匹配，任何未匹配到其它location的请求都会匹配到，相当于switch中的default</td></tr></tbody></table>

<div id="bkmrk-"></div>實際使用規則至少有三個匹配定義

```Nginx
# 直接匹配网站根，通过域名访问网站首页比较频繁，使用这个会加速处理，官网如是说。 # 这里是直接转发给后端应用服务器了，也可以是一个静态首页
# 第一个必选规则
location = / {
    proxy_pass http://tomcat:8080/index
}
# 第二个必选规则是处理静态文件请求，这是 nginx 作为 http 服务器的强项
# 有两种配置模式，目录匹配或后缀匹配，任选其一或搭配使用 
location ^~ /static/ {
    root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}
# 第三个规则就是通用规则，用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求，自己根据实际把握
# 毕竟目前的一些框架的流行，带.php、.jsp后缀的情况很少了 location / {
    proxy_pass http://tomcat:8080/
}
```

依據檔案類型設置過期時間

```Nginx
location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ {
    if (-f $request_filename) {
expires 1h;
break; }
}
```

禁止訪問某目錄

```Nginx
location ~* \.(txt|doc)${
    root /data/www/wwwroot/linuxtone/test;
    deny all;
}
```

---

### 內部命名位置

#### 命名位置的用途

- **`@` 開頭**: 這個符號用來表示這是一個內部使用的命名位置，不會與普通 URL 路徑混淆。
- **命名自由**: 你可以給命名位置起任何名稱，只要以 `@` 開頭並且不與其他位置塊或指令名稱衝突。

#### 如何使用命名位置

命名位置常用於內部重定向，當某些條件滿足時會跳轉到這些位置。通常用在 `try_files`、`error_page` 等指令中。

#### 命名位置的典型應用

- **靜態資源**: 先查找靜態文件，如果找不到則交給動態處理程序處理。
- **錯誤處理**: 如果遇到特定錯誤碼，重定向到特定的錯誤處理位置。
- **代理後備**: 如果特定條件下需要將請求代理到其他服務器。

#### 使用 `try_files` 與命名位置

```Nginx
server {
    listen 80;
    server_name example.com;

    # 主位置塊
    location / {
        # 嘗試匹配靜態文件，如果找不到則重定向到 @backend
        try_files $uri $uri/ @backend;
    }

    # 定義 @backend 內部重定向位置
    location @backend {
        # 添加自定義標頭
        add_header X-Cache-Status "MISS" always;
        # 設定代理地址
        proxy_pass http://backend_server;
    }
}

```

在這個例子中：

- `try_files $uri $uri/ @backend;` 指示 Nginx 先嘗試匹配 `$uri` 和 `$uri/`，如果都不匹配，則跳轉到 `@backend`。
- 在 `@backend` 位置塊中，添加一個自定義標頭並將請求代理到 `http://backend_server`。

#### 使用 `error_page` 與命名位置

假設你希望在遇到 404 錯誤時顯示自定義的錯誤頁面，並且如果特定條件下（例如 URL 中包含 `/api/`），將請求代理到其他服務器。可以這樣配置：

```Nginx
server {
    listen 80;
    server_name example.com;

    # 主位置塊
    location / {
        # 通常的處理邏輯
        try_files $uri $uri/ =404;
    }

    # 錯誤頁面處理
    error_page 404 /404.html;
    location = /404.html {
        # 內部重定向到 @notfound 位置
        try_files @notfound;
    }

    # 定義 @notfound 內部重定向位置
    location @notfound {
        # 檢查 URL 中是否包含 /api/
        if ($request_uri ~* /api/) {
            # 如果是，代理到 API 服務器
            proxy_pass http://api_server;
        }

        # 否則返回自定義的 404 頁面
        root /usr/share/nginx/html;
        internal; # 表示這個位置只能內部訪問
    }
}

```

# 【OpenResty】相關資源

#### 跟我学OpenResty(Nginx+Lua)开发目录贴

[https://jinnianshilongnian.iteye.com/category/333854](https://jinnianshilongnian.iteye.com/category/333854)

- [第一章 安裝OpenResty(Nginx+Lua)開發環境](https://www.pudn.com/news/628f8349bf399b7f351e7ee4.html)
- [第二章OpenResty(Nginx+Lua)開發入門](https://www.pudn.com/news/628f8349bf399b7f351e7ee6.html)
- 

#### Docker下的OpenResty三部曲

- [https://blog.csdn.net/boling\_cavalry/article/details/79290944](https://blog.csdn.net/boling_cavalry/article/details/79290944)
- [https://blog.csdn.net/boling\_cavalry/article/details/79292356](https://blog.csdn.net/boling_cavalry/article/details/79292356)
- [https://blog.csdn.net/boling\_cavalry/article/details/79311164](https://blog.csdn.net/boling_cavalry/article/details/79311164)

- [使用 OpenResty Docker 镜像快速搭建 Web 服务器 - 简书 (jianshu.com)](https://www.jianshu.com/p/b3b0bf529a0b)
- [agentzh 的 Nginx 教程](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html)
- [OpenResty 最佳实践](https://moonbingbing.gitbooks.io/openresty-best-practices/content/)
- [30天 Lua重拾筆記](https://ithelp.ithome.com.tw/users/20112470/ironman/3058)
- [Nginx Lua的执行阶段](https://blog.51cto.com/xikder/2331649)
- [Lua 錯誤處理 pcall &amp; xpcall](https://www.gushiciku.cn/pl/g0Z8/zh-tw)
- [lua 線上測試](https://www.lua.org/demo.html)
- [https://github.com/openresty/lua-nginx-module](https://bookstack.treemanou.com/lua-nginx-module)
- 第三方 lib
    
    [https://github.com/ledgetech/lua-resty-http/tree/master/lib/resty](https://github.com/ledgetech/lua-resty-http/tree/master/lib/resty)
    
    https://github.com/hamishforbes/lua-ffi-zlib/blob/master/lib/ffi-zlib.lua

# 【Openresty】Lua 的變數範圍

#### 只有在 init\_by\_lua\* 和 init\_worker\_by\_lua\* 階段才能定義真正的 全域變數  
  

# 【Openresty】Nginx Lua的 執行階段

nginx 執行步驟

```
1、post-read
读取请求内容阶段，nginx读取并解析完请求头之后就立即开始运行；例如模块 ngx_realip 就在 post-read 阶段注册了处理程序，它的功能是迫使 Nginx 认为当前请求的来源地址是指定的某一个请求头的值。

2、server-rewrite
server请求地址重写阶段；当ngx_rewrite模块的set配置指令直接书写在server配置块中时，基本上都是运行在server-rewrite 阶段

3、find-config
配置查找阶段，这个阶段并不支持Nginx模块注册处理程序，而是由Nginx核心来完成当前请求与location配置块之间的配对工作。


4、rewrite
location请求地址重写阶段，当ngx_rewrite指令用于location中，就是再这个阶段运行的；另外ngx_set_misc(设置md5、encode_base64等)模块的指令，还有ngx_lua模块的set_by_lua指令和rewrite_by_lua指令也在此阶段。

5、post-rewrite
请求地址重写提交阶段，当nginx完成rewrite阶段所要求的内部跳转动作，如果rewrite阶段有这个要求的话；

6、preaccess
访问权限检查准备阶段，ngx_limit_req和ngx_limit_zone在这个阶段运行，ngx_limit_req可以控制请求的访问频率，ngx_limit_zone可以控制访问的并发度；

7、access
访问权限检查阶段，标准模块ngx_access、第三方模块ngx_auth_request以及第三方模块ngx_lua的access_by_lua指令就运行在这个阶段。配置指令多是执行访问控制相关的任务，如检查用户的访问权限，检查用户的来源IP是否合法；

8、post-access
访问权限检查提交阶段；主要用于配合access阶段实现标准ngx_http_core模块提供的配置指令satisfy的功能。satisfy all(与关系),satisfy any(或关系)

9、try-files
配置项try_files处理阶段；专门用于实现标准配置指令try_files的功能,如果前 N-1 个参数所对应的文件系统对象都不存在，try-files 阶段就会立即发起“内部跳转”到最后一个参数(即第 N 个参数)所指定的URI.

10、content
内容产生阶段，是所有请求处理阶段中最为重要的阶段，因为这个阶段的指令通常是用来生成HTTP响应内容并输出 HTTP 响应的使命；

11、log
日志模块处理阶段；记录日志
```

```Nginx
#lua 模組使用 nginx 區塊
init_by_lua            http
set_by_lua             server, server if, location, location if
rewrite_by_lua         http, server, location, location if
access_by_lua          http, server, location, location if
content_by_lua         location, location if
header_filter_by_lua   http, server, location, location if
body_filter_by_lua     http, server, location, location if
log_by_lua             http, server, location, location if
timer
```

```Nginx
#config 區塊 基本配置
...                   # 全域性區塊
 
event{                # events 區塊
        ...
}
http{                 # http 區塊
  
    init_by_lua_block{
    }
    
    init_by_lua_file /usr/local/nginx/conf/lua/init.lua
    
    #常見的功能是執行定時任務 or health_check
    #此階段一般用來進行權限檢查和黑白名單配置
    init_worker_by_lua_block{
  
  	}
  
    #配置環境：**http，server，location，location if
  	access_by_lua_block{}
  
  	#配置環境**http，server，location，location if#
    #常用於header進行添加、刪除等操作
    header_filter_by_lua_block{}
    
    server{           # server 區塊
        location /aaa {     # location 區塊
        	set $b '';
      		#  設定變量
            set_by_lua_block $a {
                local t = 'test'
                ngx.var.b = 'test_b'
                return t
            }
            return 200 $a,$b; # test,test_b
            }
    
        location /bbb {
          set $b  '1';
      	  #rewrite_by_lua_block始終都在rewrite階段的後面執行
          rewrite_by_lua_block { ngx.var.b = '2'}
          set $b  '3';
            echo $b; #2
          }
        }
  		
  		location /ccc{
    		#**配置環境：**location，location if
    		#content_by_lua_block指令不可以和其他內容處理階段的模塊如echo、return、proxy_pass等放在一起
    		content_by_lua_block{}
  		}
  
        location /ddd{
            #content_by_lua_file可以直接获取URL中参数file_name的值
            content_by_lua_file conf/lua/$arg_file_name;
        }
  
        location /eee{
    		#将响应体全部转换为大写
    		#**配置環境：**http，server，location，location if
    		body_filter_by_lua_block { ngx.arg[1] = string.upper(ngx. arg[1]) }
        }
    }
}
```

https://blog.51cto.com/xikder/2331649

# Nginx 反向代理

```INI
server {
    listen       80;
    listen  [::]:80;
    server_name  www.srou.com;

    access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        # proxy_pass http://10.2.99.14:8081;
        index  index.html index.htm;
    }

    location /dev/ {
        proxy_pass http://10.2.99.14:8081;    
    }


    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

server {
    listen       80;
    listen  [::]:80;
    server_name  wsf.srou.com;

    location / {
        # root   /usr/share/nginx/html;
        proxy_pass http://10.2.99.14:8081;
        index  index.html index.htm;
    }

    location /uat/ {
        proxy_pass http://10.2.99.14:8082;    
    }

}

server {
    listen       80;
    listen  [::]:80;
    server_name  wsm.srou.com;

    location / {
        # root   /usr/share/nginx/html;
        proxy_pass http://10.2.99.14:8082;
        index  index.html index.htm;
    }
}


```

---

### 使用 IP 來替代域名的配置方法

1. **查找域名對應的 IP 地址**：確保你知道 `a.aaa.com` 對應的 IP 地址。
2. **修改 Nginx 配置**：將原本指向域名的 `proxy_pass` 修改為指向 IP 地址。

### 具體操作步驟：

假設 `aaa.aaa.com` 對應的 IP 地址是 `192.168.1.1`，這樣配置：

```Nginx
location / {
    proxy_pass https://192.168.1.1;
    proxy_ssl_name a.aaa.com;  # 設置 SSL 握手時使用的域名
    proxy_ssl_server_name on;    # 開啟 SNI 支持
}

```

### 配置解釋：

1. **`proxy_pass` 使用 IP 地址**：直接使用 IP 地址（如 `https://192.168.1.1`）來替代域名，這樣就不需要 DNS 解析。
2. **`proxy_ssl_name` 指令**：即使使用了 IP 地址，你仍然需要指示 Nginx 在 SSL 握手過程中使用哪個主機名。這是因為 SSL/TLS 握手過程通常依賴於域名，特別是在有 SNI（Server Name Indication）需求的情況下。
3. **`proxy_ssl_server_name on` 指令**：這個指令開啟 SNI 支持，以便在 SSL 握手時能夠發送正確的域名信息給後端服務器。

### 不使用 DNS 的注意事項

- 如果你不使用 DNS 而選擇 IP 地址，一旦服務器 IP 發生變化，你需要手動更新 Nginx 配置中的 IP 地址。
- 使用 IP 地址的配置需要確保 IP 對應的服務器上有正確的 SSL 憑證，並且該憑證包含了你在 `proxy_ssl_name` 指令中指定的域名。

這種方法有效避免了 Nginx 對 DNS 解析的依賴，但需要確保 IP 地址和 SSL 配置的正確性。

# TWCA 憑證放入 Nginx

<div id="bkmrk-"></div>### <span style="font-family: Verdana, sans-serif;">憑證安裝</span>

---

<span style="font-family: Verdana, sans-serif;">TWCA發回來憑證內容若要裝在nginx 上處理方式</span>  
<span style="font-family: Verdana, sans-serif;">解開cert.zip後會有下列檔案</span>  
<span style="font-family: Verdana, sans-serif;">  
</span> <span style="font-family: Verdana, sans-serif;">主機憑證：root.cer</span>  
<span style="font-family: Verdana, sans-serif;">網域憑證：server.cer</span>  
<span style="font-family: Verdana, sans-serif;">中繼憑證1：uca\_1.cer</span>  
<span style="font-family: Verdana, sans-serif;">中繼憑證2：uca\_2.cer</span>  
<span style="font-family: Verdana, sans-serif;">  
</span> <span style="font-family: Verdana, sans-serif;">檔案內容由上而下的順序uca\_2在上面，再來是uca\_1，透過下列指令産生檔案</span>  
<span style="background-color: white; font-size: 16px; line-height: 20px;"><span style="font-family: Verdana, sans-serif;">**cp uca\_2.cer uca.crt ; cat uca\_1.cer &gt;&gt; uca.cer**</span></span>  
<span style="font-family: Verdana, sans-serif;"><span style="background-color: white; line-height: 20px;">這個個設定可以適用於apache，apache的設定如下：</span></span>  
<span style="font-family: Verdana, sans-serif;"><span style="line-height: 20px;"> SSLEngine On</span></span>  
<span style="font-family: Verdana, sans-serif;"><span style="line-height: 20px;"> SSLCertificateFile /etc/ssl/server.cer</span></span>  
<span style="font-family: Verdana, sans-serif;"><span style="line-height: 20px;"> SSLCertificateKeyFile /</span><span style="line-height: 20px;">etc/ssl</span><span style="line-height: 20px;">/server.key</span></span>  
  
<span style="font-family: Verdana, sans-serif;"><span style="line-height: 20px;"> SSLCertificateChainFile <span style="color: red;">/</span></span><span style="color: red;"><span style="line-height: 20px;">etc/ssl</span><span style="line-height: 20px;">/uca.cer</span></span></span>  
**<span style="font-family: Verdana, sans-serif;">  
</span>** <span style="background-color: white; font-size: 16px; line-height: 20px;"><span style="font-family: Verdana, sans-serif;">若你要放在nginx上的處理方式有稍梢不同</span></span>  
<span style="font-family: Verdana, sans-serif;">nginx 上的設檔如下：</span>  
<span style="font-family: Verdana, sans-serif;"> ssl on;</span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_certificate <span style="color: red;">/etc/ssl/server.pem;</span></span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_certificate\_key /etc/ssl/server.key;</span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_session\_timeout 5m;</span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;</span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";</span>  
<span style="font-family: Verdana, sans-serif;"> ssl\_prefer\_server\_ciphers on;</span>

<div id="bkmrk--1"></div>```Nginx
server{
    listen       80;
    listen       443 ssl;
    server_name  demo.server.com.tw;

    
    ssl_certificate /etc/nginx/ssl/server.pem;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_session_timeout 5m;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
    ssl_prefer_server_ciphers on;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        # root   /usr/share/nginx/html;
        # index  index.html index.htm;    
        proxy_http_version 1.1;
    
    	# 以下是reverse proxy 才需要
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
		proxy_set_header Host $host;
		proxy_read_timeout 60s;        
        proxy_pass   http://192.168.1.1:20599/;


    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
```

<div id="bkmrk-%E5%85%B6%E4%B8%AD%2Fetc%2Fssl%2Fserver.pe"><span style="font-family: Verdana, sans-serif;">  
</span> <span style="font-family: Verdana, sans-serif;">其中/etc/ssl/server.pem和原來的産生的規則有些不同，他的順序是 server.cer + uca\_2.cer + uca\_1.cer</span>  
<span style="font-family: Verdana, sans-serif;">産生方法如下</span>  
<span style="font-family: Verdana, sans-serif;">**cp server.cer server.pem;cat uca\_2.cer &gt;&gt; server.pem ; cat uca\_1.cer &gt;&gt; server.pem**</span></div><div id="bkmrk--2"></div><div id="bkmrk-https%3A%2F%2Fdavidelin.bl">**<a>https://davidelin.blogspot.com/2014/12/nginx-twca.html</a>**</div><div id="bkmrk--3"><div>---

</div></div>### 憑證需要密碼解決方案

今天設定客戶的域名遇到 `SSL_CTX_use_PrivateKey_file`、`EVP_DecryptFinal_ex:bad decrypt error` 的錯誤訊息 指出是私鑰的問題。

<div id="bkmrk-may-07-16%3A43%3A18-terr"><div><div><div>```
May 07 16:43:18 terrylin nginx[5307]: nginx: [emerg] SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl/terryl.in/2020/server.key") failed (SSL: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt error:0May 07 16:43:18 terrylin nginx[5307]: nginx: configuration file /etc/nginx/nginx.conf test failed
May 07 16:43:18 terrylin systemd[1]: nginx.service: Control process exited, code=exited status=1
May 07 16:43:18 terrylin systemd[1]: nginx.service: Failed with result 'exit-code'.
May 07 16:43:18 terrylin systemd[1]: Failed to start A high performance web server and a reverse proxy server.
-- Subject: Unit nginx.service has failed
```

</div></div></div></div>打開私鑰檔案，發現在以往的長的不一樣，開頭多了一些編碼資訊。

<div id="bkmrk------begin-rsa-priva"><div><div><div>```
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,D5F9600F17CDAB82
```

</div></div></div></div>這是被密碼保護的私鑰。所以解決方法有兩種方式，一是補上設定在 nginx 的設定檔中。舉例，我們建立一個文字檔，命名為 `ssl_password.txt` 並將密碼存放在這個檔案。

在 Nginx 的設定中補上這一行。

<div id="bkmrk-ssl_password_file-%2Fe"><div><div><div>```
ssl_password_file /etc/nginx/ssl/terryl.in/2020/ssl_password.txt;
```

</div></div></div></div>重啟 Nginx，就可以了。

另外一種方法則是把有密碼保護的私鑰檔案轉成不需要密碼的私鑰。利用 OpenSSL 的指令如下：

<div id="bkmrk-openssl-rsa--in-serv"><div><div><div>```
openssl rsa -in server.key -out new_server.key
```

</div></div></div></div>會提示需要您輸入密碼。

<div id="bkmrk-enter-pass-phrase-fo"><div><div><div>```
Enter pass phrase for server.key:
```

</div></div></div></div>密碼正確輸入的話，則會匯出 `new_server.key`，這是不需要密碼保護的私鑰檔案。當然 Nginx 的設定就照之前的設定就可以了。

# 【Lua】錯誤處理

```Lua
local status, err = pcall(function()
    -- 可能会出现错误的代码
    local result = some_function()
    ngx.print("Result: ", result)
end)

if not status then
    -- 出现错误时的处理
    ngx.log(ngx.ERR, "Error occurred: ", err)
    ngx.status = 500
    ngx.print("An error occurred. Please try again later.")
end

```

# 【 Lua】動態參數

```Lua
function mylog(...)
	local arr = {...}
  	-- table.inset(table,index,value)
	table.insert(arr,1,"start")
	table.insert(arr,"end")
	for key,value in pairs(arr) do
		ngx.say(key..":"..value)
		-- ngx.say(value)
	end	
end

mylog("A","B","C","D")
-- 1:start
-- 2:A
-- 3:B
-- 4:C
-- 5:D
-- 6:end
```

# 【Lua】使用map 做全域變數

```Nginx
http {
    map $server_name $redis_server {
        default 10.10.10.1;
        example.com 10.10.10.2;  # 每個 server_name 可以有自己的 redis 伺服器
        example.org 10.10.10.3;
        # 添加更多的 server_name 和對應的 redis 伺服器
    }

    server {
    	listen 80 backlog=16384;
        server_name  example.com;
        charset UTF-8;
    
    	# $redis_server 10.10.10.2 
        set $REDIS_SERVER $redis_server;
        lua_need_request_body on;
        add_header X-Content-Length $response_body_size always;

        # 其他設定...
    }

     server {
    	listen 80 backlog=16384;
        server_name  example.org;
        charset UTF-8;
    
    	# $redis_server 10.10.10.3
        set $REDIS_SERVER $redis_server;
        lua_need_request_body on;
        add_header X-Content-Length $response_body_size always;

        # 其他設定...
    }
  
    server {
    	listen 80 backlog=16384;
        server_name  test.com;
        charset UTF-8;
    
    	# $redis_server 走預設 10.10.10.1
        set $REDIS_SERVER $redis_server;
        lua_need_request_body on;
        add_header X-Content-Length $response_body_size always;

        # 其他設定...
    }
    # 其他 server 區塊...
}

```

a

# 【Lua】Learn Lua in 15 Minutes

```Lua
Learn Lua in 15 Minutes
-- Two dashes start a one-line comment.

--[[
     Adding two ['s and ]'s makes it a
     multi-line comment.
--]]

----------------------------------------------------
-- 1. Variables and flow control.
----------------------------------------------------

num = 42  -- All numbers are doubles.
-- Don't freak out, 64-bit doubles have 52 bits for
-- storing exact int values; machine precision is
-- not a problem for ints that need < 52 bits.

s = 'walternate'  -- Immutable strings like Python.
t = "double-quotes are also fine"
u = [[ Double brackets
       start and end
       multi-line strings.]]
t = nil  -- Undefines t; Lua has garbage collection.

-- Blocks are denoted with keywords like do/end:
while num < 50 do
  num = num + 1  -- No ++ or += type operators.
end

-- If clauses:
if num > 40 then
  print('over 40')
elseif s ~= 'walternate' then  -- ~= is not equals.
  -- Equality check is == like Python; ok for strs.
  io.write('not over 40\n')  -- Defaults to stdout.
else
  -- Variables are global by default.
  thisIsGlobal = 5  -- Camel case is common.

  -- How to make a variable local:
  local line = io.read()  -- Reads next stdin line.

  -- String concatenation uses the .. operator:
  print('Winter is coming, ' .. line)
end

-- Undefined variables return nil.
-- This is not an error:
foo = anUnknownVariable  -- Now foo = nil.

aBoolValue = false

-- Only nil and false are falsy; 0 and '' are true!
if not aBoolValue then print('twas false') end

-- 'or' and 'and' are short-circuited.
-- This is similar to the a?b:c operator in C/js:
ans = aBoolValue and 'yes' or 'no'  --> 'no'

karlSum = 0
for i = 1, 100 do  -- The range includes both ends.
  karlSum = karlSum + i
end

-- Use "100, 1, -1" as the range to count down:
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end

-- In general, the range is begin, end[, step].

-- Another loop construct:
repeat
  print('the way of the future')
  num = num - 1
until num == 0


----------------------------------------------------
-- 2. Functions.
----------------------------------------------------

function fib(n)
  if n < 2 then return 1 end
  return fib(n - 2) + fib(n - 1)
end

-- Closures and anonymous functions are ok:
function adder(x)
  -- The returned function is created when adder is
  -- called, and remembers the value of x:
  return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16))  --> 25
print(a2(64))  --> 100

-- Returns, func calls, and assignments all work
-- with lists that may be mismatched in length.
-- Unmatched receivers are nil;
-- unmatched senders are discarded.

x, y, z = 1, 2, 3, 4
-- Now x = 1, y = 2, z = 3, and 4 is thrown away.

function bar(a, b, c)
  print(a, b, c)
  return 4, 8, 15, 16, 23, 42
end

x, y = bar('zaphod')  --> prints "zaphod  nil nil"
-- Now x = 4, y = 8, values 15..42 are discarded.

-- Functions are first-class, may be local/global.
-- These are the same:
function f(x) return x * x end
f = function (x) return x * x end

-- And so are these:
local function g(x) return math.sin(x) end
local g; g  = function (x) return math.sin(x) end
-- the 'local g' decl makes g-self-references ok.

-- Trig funcs work in radians, by the way.

-- Calls with one string param don't need parens:
print 'hello'  -- Works fine.


----------------------------------------------------
-- 3. Tables.
----------------------------------------------------

-- Tables = Lua's only compound data structure;
--          they are associative arrays.
-- Similar to php arrays or js objects, they are
-- hash-lookup dicts that can also be used as lists.

-- Using tables as dictionaries / maps:

-- Dict literals have string keys by default:
t = {key1 = 'value1', key2 = false}

-- String keys can use js-like dot notation:
print(t.key1)  -- Prints 'value1'.
t.newKey = {}  -- Adds a new key/value pair.
t.key2 = nil   -- Removes key2 from the table.

-- Literal notation for any (non-nil) value as key:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28])  -- prints "tau"

-- Key matching is basically by value for numbers
-- and strings, but by identity for tables.
a = u['@!#']  -- Now a = 'qbert'.
b = u[{}]     -- We might expect 1729, but it's nil:
-- b = nil since the lookup fails. It fails
-- because the key we used is not the same object
-- as the one used to store the original value. So
-- strings & numbers are more portable keys.

-- A one-table-param function call needs no parens:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'}  -- Prints 'Sonmi~451'.

for key, val in pairs(u) do  -- Table iteration.
  print(key, val)
end

-- _G is a special table of all globals.
print(_G['_G'] == _G)  -- Prints 'true'.

-- Using tables as lists / arrays:

-- List literals implicitly set up int keys:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do  -- #v is the size of v for lists.
  print(v[i])  -- Indices start at 1 !! SO CRAZY!
end
-- A 'list' is not a real type. v is just a table
-- with consecutive integer keys, treated as a list.

----------------------------------------------------
-- 3.1 Metatables and metamethods.
----------------------------------------------------

-- A table can have a metatable that gives the table
-- operator-overloadish behavior. Later we'll see
-- how metatables support js-prototypey behavior.

f1 = {a = 1, b = 2}  -- Represents the fraction a/b.
f2 = {a = 2, b = 3}

-- This would fail:
-- s = f1 + f2

metafraction = {}
function metafraction.__add(f1, f2)
  sum = {}
  sum.b = f1.b * f2.b
  sum.a = f1.a * f2.b + f2.a * f1.b
  return sum
end

setmetatable(f1, metafraction)
setmetatable(f2, metafraction)

s = f1 + f2  -- call __add(f1, f2) on f1's metatable

-- f1, f2 have no key for their metatable, unlike
-- prototypes in js, so you must retrieve it as in
-- getmetatable(f1). The metatable is a normal table
-- with keys that Lua knows about, like __add.

-- But the next line fails since s has no metatable:
-- t = s + s
-- Class-like patterns given below would fix this.

-- An __index on a metatable overloads dot lookups:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- works! thanks, metatable

-- Direct table lookups that fail will retry using
-- the metatable's __index value, and this recurses.

-- An __index value can also be a function(tbl, key)
-- for more customized lookups.

-- Values of __index,add, .. are called metamethods.
-- Full list. Here a is a table with the metamethod.

-- __add(a, b)                     for a + b
-- __sub(a, b)                     for a - b
-- __mul(a, b)                     for a * b
-- __div(a, b)                     for a / b
-- __mod(a, b)                     for a % b
-- __pow(a, b)                     for a ^ b
-- __unm(a)                        for -a
-- __concat(a, b)                  for a .. b
-- __len(a)                        for #a
-- __eq(a, b)                      for a == b
-- __lt(a, b)                      for a < b
-- __le(a, b)                      for a <= b
-- __index(a, b)  <fn or a table>  for a.b
-- __newindex(a, b, c)             for a.b = c
-- __call(a, ...)                  for a(...)

----------------------------------------------------
-- 3.2 Class-like tables and inheritance.
----------------------------------------------------

-- Classes aren't built in; there are different ways
-- to make them using tables and metatables.

-- Explanation for this example is below it.

Dog = {}                                   -- 1.

function Dog:new()                         -- 2.
  newObj = {sound = 'woof'}                -- 3.
  self.__index = self                      -- 4.
  return setmetatable(newObj, self)        -- 5.
end

function Dog:makeSound()                   -- 6.
  print('I say ' .. self.sound)
end

mrDog = Dog:new()                          -- 7.
mrDog:makeSound()  -- 'I say woof'         -- 8.

-- 1. Dog acts like a class; it's really a table.
-- 2. function tablename:fn(...) is the same as
--    function tablename.fn(self, ...)
--    The : just adds a first arg called self.
--    Read 7 & 8 below for how self gets its value.
-- 3. newObj will be an instance of class Dog.
-- 4. self = the class being instantiated. Often
--    self = Dog, but inheritance can change it.
--    newObj gets self's functions when we set both
--    newObj's metatable and self's __index to self.
-- 5. Reminder: setmetatable returns its first arg.
-- 6. The : works as in 2, but this time we expect
--    self to be an instance instead of a class.
-- 7. Same as Dog.new(Dog), so self = Dog in new().
-- 8. Same as mrDog.makeSound(mrDog); self = mrDog.

----------------------------------------------------

-- Inheritance example:

LoudDog = Dog:new()                           -- 1.

function LoudDog:makeSound()
  s = self.sound .. ' '                       -- 2.
  print(s .. s .. s)
end

seymour = LoudDog:new()                       -- 3.
seymour:makeSound()  -- 'woof woof woof'      -- 4.

-- 1. LoudDog gets Dog's methods and variables.
-- 2. self has a 'sound' key from new(), see 3.
-- 3. Same as LoudDog.new(LoudDog), and converted to
--    Dog.new(LoudDog) as LoudDog has no 'new' key,
--    but does have __index = Dog on its metatable.
--    Result: seymour's metatable is LoudDog, and
--    LoudDog.__index = LoudDog. So seymour.key will
--    = seymour.key, LoudDog.key, Dog.key, whichever
--    table is the first with the given key.
-- 4. The 'makeSound' key is found in LoudDog; this
--    is the same as LoudDog.makeSound(seymour).

-- If needed, a subclass's new() is like the base's:
function LoudDog:new()
  newObj = {}
  -- set up newObj
  self.__index = self
  return setmetatable(newObj, self)
end

----------------------------------------------------
-- 4. Modules.
----------------------------------------------------


--[[ I'm commenting out this section so the rest of
--   this script remains runnable.
-- Suppose the file mod.lua looks like this:
local M = {}

local function sayMyName()
  print('Hrunkner')
end

function M.sayHello()
  print('Why hello there')
  sayMyName()
end

return M

-- Another file can use mod.lua's functionality:
local mod = require('mod')  -- Run the file mod.lua.

-- require is the standard way to include modules.
-- require acts like:     (if not cached; see below)
local mod = (function ()
  <contents of mod.lua>
end)()
-- It's like mod.lua is a function body, so that
-- locals inside mod.lua are invisible outside it.

-- This works because mod here = M in mod.lua:
mod.sayHello()  -- Says hello to Hrunkner.

-- This is wrong; sayMyName only exists in mod.lua:
mod.sayMyName()  -- error

-- require's return values are cached so a file is
-- run at most once, even when require'd many times.

-- Suppose mod2.lua contains "print('Hi!')".
local a = require('mod2')  -- Prints Hi!
local b = require('mod2')  -- Doesn't print; a=b.

-- dofile is like require without caching:
dofile('mod2.lua')  --> Hi!
dofile('mod2.lua')  --> Hi! (runs it again)

-- loadfile loads a lua file but doesn't run it yet.
f = loadfile('mod2.lua')  -- Call f() to run it.

-- loadstring is loadfile for strings.
g = loadstring('print(343)')  -- Returns a function.
g()  -- Prints out 343; nothing printed before now.

--]]

----------------------------------------------------
-- 5. References.
----------------------------------------------------

--[[

I was excited to learn Lua so I could make games
with the Löve 2D game engine. That's the why.

I started with BlackBulletIV's Lua for programmers.
Next I read the official Programming in Lua book.
That's the how.

It might be helpful to check out the Lua short
reference on lua-users.org.

The main topics not covered are standard libraries:
 * string library
 * table library
 * math library
 * io library
 * os library

By the way, this entire file is valid Lua; save it
as learn.lua and run it with "lua learn.lua" !

This was first written for tylerneylon.com. It's
also available as a github gist. Tutorials for other
languages, in the same style as this one, are here:

https://learnxinyminutes.com/

Have fun with Lua!

--]]

Tyler Neylon
336.2013
```