1 Star 0 Fork 0

流动de云 / ngx_healthcheck_module

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
ngx_http_upstream_check_module.c 109.52 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865
/*
* Copyright (C) 2017- Changxun Zhou(changxunzhou@qq.com)
* desc: http upstream server health check.
* note: base on https://github.com/yaoweibin/nginx_upstream_check_module
*/
/* Thanks:
* Copyright (C) 2010-2014 Weibin Yao (yaoweibin@gmail.com)
* Copyright (C) 2010-2014 Alibaba Group Holding Limited
*/
#include "common.h.in"
#include "ngx_http_upstream_check_module.h"
#define LOG_LEVEL NGX_LOG_DEBUG_HTTP
#define MODULE_NAME "[ngx_healthcheck:http]"
#pragma pack(push, 1)
typedef struct {
u_char major;
u_char minor;
} ngx_ssl_protocol_version_t;
typedef struct {
u_char msg_type;
ngx_ssl_protocol_version_t version;
uint16_t length;
u_char handshake_type;
u_char handshake_length[3];
ngx_ssl_protocol_version_t hello_version;
time_t time;
u_char random[28];
u_char others[0];
} ngx_ssl_server_hello_t;
typedef struct {
u_char packet_length[3];
u_char packet_number;
u_char protocol_version;
u_char others[0];
} ngx_mysql_handshake_init_t;
typedef struct {
uint16_t preamble;
uint16_t length;
u_char type;
} ngx_ajp_raw_packet_t;
#pragma pack(pop)
typedef struct {
ngx_buf_t send;
ngx_buf_t recv;
ngx_uint_t state;
ngx_http_status_t status;
size_t padding;
size_t length;
} ngx_http_upstream_check_ctx_t;
#define NGX_HTTP_CHECK_CONNECT_DONE 0x0001
#define NGX_HTTP_CHECK_SEND_DONE 0x0002
#define NGX_HTTP_CHECK_RECV_DONE 0x0004
#define NGX_HTTP_CHECK_ALL_DONE 0x0008
#define NGX_HTTP_CHECK_TCP 0x0001
#define NGX_HTTP_CHECK_HTTP 0x0002
#define NGX_HTTP_CHECK_SSL_HELLO 0x0004
#define NGX_HTTP_CHECK_MYSQL 0x0008
#define NGX_HTTP_CHECK_AJP 0x0010
#define NGX_CHECK_HTTP_2XX 0x0002
#define NGX_CHECK_HTTP_3XX 0x0004
#define NGX_CHECK_HTTP_4XX 0x0008
#define NGX_CHECK_HTTP_5XX 0x0010
#define NGX_CHECK_HTTP_ERR 0x8000
typedef void (*ngx_http_upstream_check_status_format_pt) (ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag);
typedef struct {
ngx_str_t format;
ngx_str_t content_type;
ngx_http_upstream_check_status_format_pt output;
} ngx_check_status_conf_t;
#define NGX_CHECK_STATUS_DOWN 0x0001
#define NGX_CHECK_STATUS_UP 0x0002
typedef struct {
ngx_check_status_conf_t *format;
ngx_flag_t flag;
} ngx_http_upstream_check_status_ctx_t;
typedef ngx_int_t (*ngx_http_upstream_check_status_command_pt)
(ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
typedef struct {
ngx_str_t name;
ngx_http_upstream_check_status_command_pt handler;
} ngx_check_status_command_t;
typedef struct {
ngx_uint_t check_shm_size;
ngx_upstream_check_peers_t *peers;
} ngx_http_upstream_check_main_conf_t;
typedef struct {
ngx_check_status_conf_t *format;
} ngx_http_upstream_check_loc_conf_t;
typedef struct {
u_char version;
u_char type;
u_char request_id_hi;
u_char request_id_lo;
u_char content_length_hi;
u_char content_length_lo;
u_char padding_length;
u_char reserved;
} ngx_http_fastcgi_header_t;
typedef struct {
u_char role_hi;
u_char role_lo;
u_char flags;
u_char reserved[5];
} ngx_http_fastcgi_begin_request_t;
typedef struct {
u_char version;
u_char type;
u_char request_id_hi;
u_char request_id_lo;
} ngx_http_fastcgi_header_small_t;
typedef struct {
ngx_http_fastcgi_header_t h0;
ngx_http_fastcgi_begin_request_t br;
ngx_http_fastcgi_header_small_t h1;
} ngx_http_fastcgi_request_start_t;
#define NGX_HTTP_FASTCGI_RESPONDER 1
#define NGX_HTTP_FASTCGI_KEEP_CONN 1
#define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
#define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
#define NGX_HTTP_FASTCGI_END_REQUEST 3
#define NGX_HTTP_FASTCGI_PARAMS 4
#define NGX_HTTP_FASTCGI_STDIN 5
#define NGX_HTTP_FASTCGI_STDOUT 6
#define NGX_HTTP_FASTCGI_STDERR 7
#define NGX_HTTP_FASTCGI_DATA 8
typedef enum {
ngx_http_fastcgi_st_version = 0,
ngx_http_fastcgi_st_type,
ngx_http_fastcgi_st_request_id_hi,
ngx_http_fastcgi_st_request_id_lo,
ngx_http_fastcgi_st_content_length_hi,
ngx_http_fastcgi_st_content_length_lo,
ngx_http_fastcgi_st_padding_length,
ngx_http_fastcgi_st_reserved,
ngx_http_fastcgi_st_data,
ngx_http_fastcgi_st_padding
} ngx_http_fastcgi_state_e;
static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
{ 1, /* version */
NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
0, /* request_id_hi */
1, /* request_id_lo */
0, /* content_length_hi */
sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
0, /* padding_length */
0 }, /* reserved */
{ 0, /* role_hi */
NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
0, /* NGX_HTTP_FASTCGI_KEEP_CONN */ /* flags */
{ 0, 0, 0, 0, 0 } }, /* reserved[5] */
{ 1, /* version */
NGX_HTTP_FASTCGI_PARAMS, /* type */
0, /* request_id_hi */
1 }, /* request_id_lo */
};
static ngx_int_t ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle);
static ngx_int_t ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c);
static void ngx_http_upstream_check_begin_handler(ngx_event_t *event);
static void ngx_http_upstream_check_connect_handler(ngx_event_t *event);
static void ngx_http_upstream_check_peek_handler(ngx_event_t *event);
static void ngx_http_upstream_check_send_handler(ngx_event_t *event);
static void ngx_http_upstream_check_recv_handler(ngx_event_t *event);
static void ngx_http_upstream_check_discard_handler(ngx_event_t *event);
static void ngx_http_upstream_check_dummy_handler(ngx_event_t *event);
static ngx_int_t ngx_http_upstream_check_http_init(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_http_parse(
ngx_upstream_check_peer_t *peer);
ngx_int_t ngx_upstream_check_http_parse_status_line(
ngx_buf_t *b, ngx_uint_t *pstate, ngx_http_status_t *status);
ngx_int_t ngx_upstream_check_http_body_regex(
ngx_conf_t *cf,
ngx_upstream_check_srv_conf_t *ucscf,
ngx_str_t *regex, ngx_uint_t caseless);
static void ngx_http_upstream_check_http_reinit(
ngx_upstream_check_peer_t *peer);
static ngx_buf_t *ngx_http_upstream_check_create_fastcgi_request(
ngx_pool_t *pool, ngx_str_t *params, ngx_uint_t num);
static ngx_int_t ngx_http_upstream_check_fastcgi_parse(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_fastcgi_process_record(
ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,
ngx_http_status_t *status);
static ngx_int_t ngx_http_upstream_check_parse_fastcgi_status(
ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,
ngx_http_status_t *status);
static ngx_int_t ngx_http_upstream_check_ssl_hello_init(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_ssl_hello_parse(
ngx_upstream_check_peer_t *peer);
static void ngx_http_upstream_check_ssl_hello_reinit(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_mysql_init(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_mysql_parse(
ngx_upstream_check_peer_t *peer);
static void ngx_http_upstream_check_mysql_reinit(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_ajp_init(
ngx_upstream_check_peer_t *peer);
static ngx_int_t ngx_http_upstream_check_ajp_parse(
ngx_upstream_check_peer_t *peer);
static void ngx_http_upstream_check_ajp_reinit(
ngx_upstream_check_peer_t *peer);
static void ngx_http_upstream_check_status_update(
ngx_upstream_check_peer_t *peer,
ngx_int_t result);
static void ngx_http_upstream_check_clean_event(
ngx_upstream_check_peer_t *peer);
static void ngx_http_upstream_check_timeout_handler(ngx_event_t *event);
static void ngx_http_upstream_check_finish_handler(ngx_event_t *event);
static ngx_int_t ngx_http_upstream_check_need_exit();
static void ngx_http_upstream_check_clear_all_events();
static ngx_int_t ngx_http_upstream_check_status_handler(
ngx_http_request_t *r);
static void ngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,
ngx_http_upstream_check_status_ctx_t *ctx);
static ngx_int_t ngx_http_upstream_check_status_command_format(
ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
static ngx_int_t ngx_http_upstream_check_status_command_status(
ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
static void ngx_http_upstream_check_status_html_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag);
static void ngx_http_upstream_check_status_csv_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag);
static void ngx_http_upstream_check_status_json_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag);
static ngx_int_t ngx_http_upstream_check_addr_change_port(ngx_pool_t *pool,
ngx_addr_t *dst, ngx_addr_t *src, ngx_uint_t port);
static ngx_check_conf_t *ngx_http_get_check_type_conf(ngx_str_t *str);
static char *ngx_http_upstream_check(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_upstream_check_http_send(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_upstream_check_http_body(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_upstream_check_shm_size(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static ngx_check_status_conf_t *ngx_http_get_check_status_format_conf(
ngx_str_t *str);
static char *ngx_http_upstream_check_status(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static void *ngx_http_upstream_check_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf,
void *conf);
static void *ngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf);
static char * ngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
#define SHM_NAME_LEN 256
static char *ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf);
static ngx_int_t ngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name,
ngx_pool_t *pool, ngx_uint_t generation);
static ngx_shm_zone_t *ngx_shared_memory_find(ngx_cycle_t *cycle,
ngx_str_t *name, void *tag);
static ngx_upstream_check_peer_shm_t *
ngx_http_upstream_check_find_shm_peer(ngx_upstream_check_peers_shm_t *peers_shm,
ngx_addr_t *addr);
static ngx_int_t ngx_http_upstream_check_init_shm_peer(
ngx_upstream_check_peer_shm_t *peer_shm,
ngx_upstream_check_peer_shm_t *opeer_shm,
ngx_uint_t init_down, ngx_pool_t *pool, ngx_str_t *peer_name);
static ngx_int_t ngx_http_upstream_check_init_shm_zone(
ngx_shm_zone_t *shm_zone, void *data);
static ngx_int_t ngx_http_upstream_check_init_process(ngx_cycle_t *cycle);
static ngx_conf_bitmask_t ngx_check_http_expect_alive_masks[] = {
{ ngx_string("http_2xx"), NGX_CHECK_HTTP_2XX },
{ ngx_string("http_3xx"), NGX_CHECK_HTTP_3XX },
{ ngx_string("http_4xx"), NGX_CHECK_HTTP_4XX },
{ ngx_string("http_5xx"), NGX_CHECK_HTTP_5XX },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_upstream_check_commands[] = {
{ ngx_string("check"),
NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
ngx_http_upstream_check,
0,
0,
NULL },
{ ngx_string("check_keepalive_requests"),
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
ngx_http_upstream_check_keepalive_requests,
0,
0,
NULL },
{ ngx_string("check_http_send"),
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
ngx_http_upstream_check_http_send,
0,
0,
NULL },
{ ngx_string("check_http_expect_alive"),
NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
ngx_http_upstream_check_http_expect_alive,
0,
0,
NULL },
{ ngx_string("check_http_expect_body"),
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE2,
ngx_upstream_check_http_body,
0,
0,
NULL },
{ ngx_string("check_fastcgi_param"),
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE2,
ngx_http_upstream_check_fastcgi_params,
0,
0,
NULL },
{ ngx_string("check_shm_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_upstream_check_shm_size,
0,
0,
NULL },
{ ngx_string("check_status"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1|NGX_CONF_NOARGS,
ngx_http_upstream_check_status,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_upstream_check_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_upstream_check_create_main_conf,/* create main configuration */
ngx_http_upstream_check_init_main_conf, /* init main configuration */
ngx_http_upstream_check_create_srv_conf, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_upstream_check_create_loc_conf, /* create location configuration */
ngx_http_upstream_check_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_upstream_check_module = {
NGX_MODULE_V1,
&ngx_http_upstream_check_module_ctx, /* module context */
ngx_http_upstream_check_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_http_upstream_check_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t fastcgi_default_request;
static ngx_str_t fastcgi_default_params[] = {
ngx_string("REQUEST_METHOD"), ngx_string("GET"),
ngx_string("REQUEST_URI"), ngx_string("/"),
ngx_string("SCRIPT_FILENAME"), ngx_string("index.php"),
};
#define NGX_SSL_RANDOM "NGX_HTTP_CHECK_SSL_HELLO\n\n\n\n"
/*
* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
* check type of ssl_hello to ensure that the remote server speaks SSL.
*
* Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
*/
static char sslv3_client_hello_pkt[] = {
"\x16" /* ContentType : 0x16 = Hanshake */
"\x03\x01" /* ProtocolVersion : 0x0301 = TLSv1.0 */
"\x00\x6f" /* ContentLength : 0x6f bytes after this one */
"\x01" /* HanshakeType : 0x01 = CLIENT HELLO */
"\x00\x00\x6b" /* HandshakeLength : 0x6b bytes after this one */
"\x03\x03" /* Hello Version : 0x0303 = TLSv1.2 */
"\x00\x00\x00\x00" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
NGX_SSL_RANDOM /* Random : must be exactly 28 bytes */
"\x00" /* Session ID length : empty (no session ID) */
"\x00\x1a" /* Cipher Suite Length : \x1a bytes after this one */
"\xc0\x2b" "\xc0\x2f" "\xcc\xa9" "\xcc\xa8" /* 13 modern ciphers */
"\xc0\x0a" "\xc0\x09" "\xc0\x13" "\xc0\x14"
"\x00\x33" "\x00\x39" "\x00\x2f" "\x00\x35"
"\x00\x0a"
"\x01" /* Compression Length : 0x01 = 1 byte for types */
"\x00" /* Compression Type : 0x00 = NULL compression */
"\x00\x28" /* Extensions length */
"\x00\x0a" /* EC extension */
"\x00\x08" /* extension length */
"\x00\x06" /* curves length */
"\x00\x17" "\x00\x18" "\x00\x19" /* Three curves */
"\x00\x0d" /* Signature extension */
"\x00\x18" /* extension length */
"\x00\x16" /* hash list length */
"\x04\x01" "\x05\x01" "\x06\x01" "\x02\x01" /* 11 hash algorithms */
"\x04\x03" "\x05\x03" "\x06\x03" "\x02\x03"
"\x05\x02" "\x04\x02" "\x02\x02"
};
#define NGX_SSL_HANDSHAKE 0x16
#define NGX_SSL_SERVER_HELLO 0x02
#define NGX_AJP_CPING 0x0a
#define NGX_AJP_CPONG 0x09
static char ngx_ajp_cping_packet[] = {
0x12, 0x34, 0x00, 0x01, NGX_AJP_CPING, 0x00
};
static char ngx_ajp_cpong_packet[] = {
0x41, 0x42, 0x00, 0x01, NGX_AJP_CPONG
};
static ngx_check_conf_t ngx_check_types[] = {
{ NGX_HTTP_CHECK_TCP,
ngx_string("tcp"),
ngx_null_string,
0,
ngx_http_upstream_check_peek_handler,
ngx_http_upstream_check_peek_handler,
NULL,
NULL,
NULL,
0,
1 },
{ NGX_HTTP_CHECK_HTTP,
ngx_string("http"),
ngx_string("GET / HTTP/1.0\r\n\r\n"),
NGX_CONF_BITMASK_SET | NGX_CHECK_HTTP_2XX | NGX_CHECK_HTTP_3XX,
ngx_http_upstream_check_send_handler,
ngx_http_upstream_check_recv_handler,
ngx_http_upstream_check_http_init,
ngx_http_upstream_check_http_parse,
ngx_http_upstream_check_http_reinit,
1,
1 },
{ NGX_HTTP_CHECK_HTTP,
ngx_string("fastcgi"),
ngx_null_string,
0,
ngx_http_upstream_check_send_handler,
ngx_http_upstream_check_recv_handler,
ngx_http_upstream_check_http_init,
ngx_http_upstream_check_fastcgi_parse,
ngx_http_upstream_check_http_reinit,
1,
0 },
{ NGX_HTTP_CHECK_SSL_HELLO,
ngx_string("ssl_hello"),
ngx_string(sslv3_client_hello_pkt),
0,
ngx_http_upstream_check_send_handler,
ngx_http_upstream_check_recv_handler,
ngx_http_upstream_check_ssl_hello_init,
ngx_http_upstream_check_ssl_hello_parse,
ngx_http_upstream_check_ssl_hello_reinit,
1,
0 },
{ NGX_HTTP_CHECK_MYSQL,
ngx_string("mysql"),
ngx_null_string,
0,
ngx_http_upstream_check_send_handler,
ngx_http_upstream_check_recv_handler,
ngx_http_upstream_check_mysql_init,
ngx_http_upstream_check_mysql_parse,
ngx_http_upstream_check_mysql_reinit,
1,
0 },
{ NGX_HTTP_CHECK_AJP,
ngx_string("ajp"),
ngx_string(ngx_ajp_cping_packet),
0,
ngx_http_upstream_check_send_handler,
ngx_http_upstream_check_recv_handler,
ngx_http_upstream_check_ajp_init,
ngx_http_upstream_check_ajp_parse,
ngx_http_upstream_check_ajp_reinit,
1,
0 },
{ 0,
ngx_null_string,
ngx_null_string,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
0 }
};
static ngx_check_status_conf_t ngx_check_status_formats[] = {
{ ngx_string("html"),
ngx_string("text/html"),
ngx_http_upstream_check_status_html_format },
{ ngx_string("csv"),
ngx_string("text/plain"),
ngx_http_upstream_check_status_csv_format },
{ ngx_string("json"),
ngx_string("application/json"), /* RFC 4627 */
ngx_http_upstream_check_status_json_format },
{ ngx_null_string, ngx_null_string, NULL }
};
static ngx_check_status_command_t ngx_check_status_commands[] = {
{ ngx_string("format"),
ngx_http_upstream_check_status_command_format },
{ ngx_string("status"),
ngx_http_upstream_check_status_command_status },
{ ngx_null_string, NULL }
};
static ngx_uint_t ngx_http_upstream_check_shm_generation = 0;
ngx_upstream_check_peers_t *http_peers_ctx = NULL;
ngx_uint_t
ngx_http_upstream_check_add_peer(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer_addr)
{
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_peers_t *peers;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_http_upstream_check_main_conf_t *ucmcf;
if (us->srv_conf == NULL) {
return NGX_ERROR;
}
ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);
if(ucscf->check_interval == 0) {
return NGX_ERROR;
}
ucmcf = ngx_http_conf_get_module_main_conf(cf,
ngx_http_upstream_check_module);
peers = ucmcf->peers;
peer = ngx_array_push(&peers->peers);
if (peer == NULL) {
return NGX_ERROR;
}
ngx_memzero(peer, sizeof(ngx_upstream_check_peer_t));
peer->index = peers->peers.nelts - 1;
peer->conf = ucscf;
peer->upstream_name = &us->host;
peer->peer_addr = peer_addr;
if (ucscf->port) {
peer->check_peer_addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t));
if (peer->check_peer_addr == NULL) {
return NGX_ERROR;
}
if (ngx_http_upstream_check_addr_change_port(cf->pool,
peer->check_peer_addr, peer_addr, ucscf->port)
!= NGX_OK) {
return NGX_ERROR;
}
} else {
peer->check_peer_addr = peer->peer_addr;
}
peers->checksum +=
ngx_murmur_hash2(peer_addr->name.data, peer_addr->name.len);
return peer->index;
}
static ngx_int_t
ngx_http_upstream_check_addr_change_port(ngx_pool_t *pool, ngx_addr_t *dst,
ngx_addr_t *src, ngx_uint_t port)
{
size_t len;
u_char *p;
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
dst->socklen = src->socklen;
dst->sockaddr = ngx_palloc(pool, dst->socklen);
if (dst->sockaddr == NULL) {
return NGX_ERROR;
}
ngx_memcpy(dst->sockaddr, src->sockaddr, dst->socklen);
switch (dst->sockaddr->sa_family) {
case AF_INET:
len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
sin = (struct sockaddr_in *) dst->sockaddr;
sin->sin_port = htons(port);
break;
#if (NGX_HAVE_INET6)
case AF_INET6:
len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
sin6 = (struct sockaddr_in6 *) dst->sockaddr;
sin6->sin6_port = htons(port);
break;
#endif
default:
return NGX_ERROR;
}
p = ngx_pnalloc(pool, len);
if (p == NULL) {
return NGX_ERROR;
}
#if (nginx_version >= 1005012)
len = ngx_sock_ntop(dst->sockaddr, dst->socklen, p, len, 1);
#else
len = ngx_sock_ntop(dst->sockaddr, p, len, 1);
#endif
dst->name.len = len;
dst->name.data = p;
return NGX_OK;
}
ngx_uint_t
ngx_http_upstream_check_peer_down(ngx_uint_t index)
{
ngx_upstream_check_peer_t *peer;
if (http_peers_ctx == NULL || index >= http_peers_ctx->peers.nelts) {
return 0;
}
peer = http_peers_ctx->peers.elts;
return (peer[index].shm->down);
}
/* TODO: this interface can count each peer's busyness */
void
ngx_http_upstream_check_get_peer(ngx_uint_t index)
{
ngx_upstream_check_peer_t *peer;
if (http_peers_ctx == NULL || index >= http_peers_ctx->peers.nelts) {
return;
}
peer = http_peers_ctx->peers.elts;
ngx_shmtx_lock(&peer[index].shm->mutex);
peer[index].shm->busyness++;
peer[index].shm->access_count++;
ngx_shmtx_unlock(&peer[index].shm->mutex);
}
void
ngx_http_upstream_check_free_peer(ngx_uint_t index)
{
ngx_upstream_check_peer_t *peer;
if (http_peers_ctx == NULL || index >= http_peers_ctx->peers.nelts) {
return;
}
peer = http_peers_ctx->peers.elts;
ngx_shmtx_lock(&peer[index].shm->mutex);
if (peer[index].shm->busyness > 0) {
peer[index].shm->busyness--;
}
ngx_shmtx_unlock(&peer[index].shm->mutex);
}
static ngx_int_t
ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle)
{
ngx_uint_t i;
ngx_msec_t t, delay;
ngx_check_conf_t *cf;
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_peers_t *peers;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_upstream_check_peer_shm_t *peer_shm;
ngx_upstream_check_peers_shm_t *peers_shm;
peers = http_peers_ctx;
if (peers == NULL) {
return NGX_OK;
}
peers_shm = peers->peers_shm;
if (peers_shm == NULL) {
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cycle->log, 0, MODULE_NAME
"http check upstream init_process, shm_name: %V, "
"peer number: %ud",
&peers->check_shm_name,
peers->peers.nelts);
srandom(ngx_pid);
peer = peers->peers.elts;
peer_shm = peers_shm->peers;
for (i = 0; i < peers->peers.nelts; i++) {
peer[i].shm = &peer_shm[i];
peer[i].check_ev.handler = ngx_http_upstream_check_begin_handler;
peer[i].check_ev.log = cycle->log;
peer[i].check_ev.data = &peer[i];
peer[i].check_ev.timer_set = 0;
peer[i].check_timeout_ev.handler =
ngx_http_upstream_check_timeout_handler;
peer[i].check_timeout_ev.log = cycle->log;
peer[i].check_timeout_ev.data = &peer[i];
peer[i].check_timeout_ev.timer_set = 0;
ucscf = peer[i].conf;
cf = ucscf->check_type_conf;
if (cf->need_pool) {
peer[i].pool = ngx_create_pool(ngx_pagesize, cycle->log);
if (peer[i].pool == NULL) {
return NGX_ERROR;
}
}
peer[i].send_handler = cf->send_handler;
peer[i].recv_handler = cf->recv_handler;
peer[i].init = cf->init;
peer[i].parse = cf->parse;
peer[i].reinit = cf->reinit;
/*
* We add a random start time here, since we don't want to trigger
* the check events too close to each other at the beginning.
*/
delay = ucscf->check_interval > 1000 ? ucscf->check_interval : 1000;
t = ngx_random() % delay;
ngx_add_timer(&peer[i].check_ev, t);
}
return NGX_OK;
}
static void
ngx_http_upstream_check_begin_handler(ngx_event_t *event)
{
ngx_msec_t interval;
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_peers_t *peers;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_upstream_check_peers_shm_t *peers_shm;
if (ngx_http_upstream_check_need_exit()) {
return;
}
peers = http_peers_ctx;
if (peers == NULL) {
return;
}
peers_shm = peers->peers_shm;
if (peers_shm == NULL) {
return;
}
peer = event->data;
ucscf = peer->conf;
ngx_add_timer(event, ucscf->check_interval / 2);
/* This process is processing this peer now. */
if ((peer->shm->owner == ngx_pid ||
peer->check_timeout_ev.timer_set)) {
return;
}
interval = ngx_current_msec - peer->shm->access_time;
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, event->log, 0, MODULE_NAME
"http check begin handler index: %ui, owner: %P, "
"ngx_pid: %P, interval: %M, check_interval: %M",
peer->index, peer->shm->owner,
ngx_pid, interval,
ucscf->check_interval);
ngx_shmtx_lock(&peer->shm->mutex);
if (peers_shm->generation != ngx_http_upstream_check_shm_generation) {
ngx_shmtx_unlock(&peer->shm->mutex);
return;
}
if ((interval >= ucscf->check_interval)
&& (peer->shm->owner == NGX_INVALID_PID))
{
peer->shm->owner = ngx_pid;
} else if (interval >= (ucscf->check_interval << 4)) {
/*
* If the check peer has been untouched for 2^4 times of
* the check interval, activate the current timer.
* Sometimes, the checking process may disappear
* in some circumstances, and the clean event will never
* be triggered.
*/
peer->shm->owner = ngx_pid;
peer->shm->access_time = ngx_current_msec;
}
ngx_shmtx_unlock(&peer->shm->mutex);
if (peer->shm->owner == ngx_pid) {
ngx_http_upstream_check_connect_handler(event);
}
}
static void
ngx_http_upstream_check_connect_handler(ngx_event_t *event)
{
ngx_int_t rc;
ngx_connection_t *c;
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_srv_conf_t *ucscf;
if (ngx_http_upstream_check_need_exit()) {
return;
}
peer = event->data;
ucscf = peer->conf;
if (peer->pc.connection != NULL) {
c = peer->pc.connection;
if ((rc = ngx_http_upstream_check_peek_one_byte(c)) == NGX_OK) {
goto upstream_check_connect_done;
} else {
ngx_close_connection(c);
peer->pc.connection = NULL;
}
}
ngx_memzero(&peer->pc, sizeof(ngx_peer_connection_t));
peer->pc.sockaddr = peer->check_peer_addr->sockaddr;
peer->pc.socklen = peer->check_peer_addr->socklen;
peer->pc.name = &peer->check_peer_addr->name;
peer->pc.get = ngx_event_get_peer;
peer->pc.log = event->log;
peer->pc.log_error = NGX_ERROR_ERR;
peer->pc.cached = 0;
peer->pc.connection = NULL;
rc = ngx_event_connect_peer(&peer->pc);
if (rc == NGX_ERROR || rc == NGX_DECLINED) {
ngx_http_upstream_check_status_update(peer, 0);
ngx_http_upstream_check_clean_event(peer);
return;
}
/* NGX_OK or NGX_AGAIN */
c = peer->pc.connection;
c->data = peer;
c->log = peer->pc.log;
c->sendfile = 0;
c->read->log = c->log;
c->write->log = c->log;
c->pool = peer->pool;
upstream_check_connect_done:
peer->state = NGX_HTTP_CHECK_CONNECT_DONE;
c->write->handler = peer->send_handler;
c->read->handler = peer->recv_handler;
ngx_add_timer(&peer->check_timeout_ev, ucscf->check_timeout);
/* The kqueue's loop interface needs it. */
if (rc == NGX_OK) {
c->write->handler(c->write);
}
}
static ngx_int_t
ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c)
{
char buf[1];
ngx_int_t n;
ngx_err_t err;
n = recv(c->fd, buf, 1, MSG_PEEK);
err = ngx_socket_errno;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,
"http check upstream recv(): %i, fd: %d",
n, c->fd);
if (n == 1 || (n == -1 && err == NGX_EAGAIN)) {
return NGX_OK;
} else {
return NGX_ERROR;
}
}
static void
ngx_http_upstream_check_peek_handler(ngx_event_t *event)
{
ngx_connection_t *c;
ngx_upstream_check_peer_t *peer;
if (ngx_http_upstream_check_need_exit()) {
return;
}
c = event->data;
peer = c->data;
if (ngx_http_upstream_check_peek_one_byte(c) == NGX_OK) {
ngx_http_upstream_check_status_update(peer, 1);
} else {
c->error = 1;
ngx_http_upstream_check_status_update(peer, 0);
}
ngx_http_upstream_check_clean_event(peer);
ngx_http_upstream_check_finish_handler(event);
}
static void
ngx_http_upstream_check_discard_handler(ngx_event_t *event)
{
u_char buf[4096];
ssize_t size;
ngx_connection_t *c;
ngx_upstream_check_peer_t *peer;
c = event->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME
"upstream check discard handler");
if (ngx_http_upstream_check_need_exit()) {
return;
}
peer = c->data;
while (1) {
size = c->recv(c, buf, 4096);
if (size > 0) {
continue;
} else if (size == NGX_AGAIN) {
break;
} else {
if (size == 0) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME
"peer closed its half side of the connection");
}
goto check_discard_fail;
}
}
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
goto check_discard_fail;
}
return;
check_discard_fail:
c->error = 1;
ngx_http_upstream_check_clean_event(peer);
}
static void
ngx_http_upstream_check_dummy_handler(ngx_event_t *event)
{
return;
}
static void
ngx_http_upstream_check_send_handler(ngx_event_t *event)
{
ssize_t size;
ngx_connection_t *c;
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_peer_t *peer;
if (ngx_http_upstream_check_need_exit()) {
return;
}
c = event->data;
peer = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME "http check send.");
if (c->pool == NULL) {
ngx_log_error(NGX_LOG_ERR, event->log, 0, MODULE_NAME
"check pool NULL with peer: %V ",
&peer->check_peer_addr->name);
goto check_send_fail;
}
if (peer->state != NGX_HTTP_CHECK_CONNECT_DONE) {
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, event->log, 0, MODULE_NAME
"check handle write event error with peer: %V ",
&peer->check_peer_addr->name);
goto check_send_fail;
}
return;
}
if (peer->check_data == NULL) {
peer->check_data = ngx_pcalloc(peer->pool,
sizeof(ngx_http_upstream_check_ctx_t));
if (peer->check_data == NULL) {
goto check_send_fail;
}
if (peer->init == NULL || peer->init(peer) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, event->log, 0, MODULE_NAME
"check init error with peer: %V ",
&peer->check_peer_addr->name);
goto check_send_fail;
}
}
ctx = peer->check_data;
while (ctx->send.pos < ctx->send.last) {
size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos);
#if (NGX_DEBUG)
{
ngx_err_t err;
err = (size >=0) ? 0 : ngx_socket_errno;
ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, err,
"http check send size: %z, total: %z",
size, ctx->send.last - ctx->send.pos);
}
#endif
if (size > 0) {
ctx->send.pos += size;
} else if (size == 0 || size == NGX_AGAIN) {
return;
} else {
c->error = 1;
goto check_send_fail;
}
}
if (ctx->send.pos == ctx->send.last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME "http check send done.");
peer->state = NGX_HTTP_CHECK_SEND_DONE;
c->requests++;
}
return;
check_send_fail:
ngx_http_upstream_check_status_update(peer, 0);
ngx_http_upstream_check_clean_event(peer);
}
static void
ngx_http_upstream_check_recv_handler(ngx_event_t *event)
{
u_char *new_buf;
ssize_t size, n;
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_peer_t *peer;
if (ngx_http_upstream_check_need_exit()) {
return;
}
c = event->data;
peer = c->data;
if (peer->state != NGX_HTTP_CHECK_SEND_DONE) {
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
goto check_recv_fail;
}
return;
}
ctx = peer->check_data;
if (ctx->recv.start == NULL) {
/* 1/2 of the page_size, is it enough? */
ctx->recv.start = ngx_palloc(c->pool, ngx_pagesize / 2);
if (ctx->recv.start == NULL) {
goto check_recv_fail;
}
ctx->recv.last = ctx->recv.pos = ctx->recv.start;
ctx->recv.end = ctx->recv.start + ngx_pagesize / 2;
}
while (1) {
n = ctx->recv.end - ctx->recv.last;
/* buffer not big enough? enlarge it by twice */
if (n == 0) {
size = ctx->recv.end - ctx->recv.start;
new_buf = ngx_palloc(c->pool, size * 2);
if (new_buf == NULL) {
goto check_recv_fail;
}
ngx_memcpy(new_buf, ctx->recv.start, size);
ctx->recv.pos = ctx->recv.start = new_buf;
ctx->recv.last = new_buf + size;
ctx->recv.end = new_buf + size * 2;
n = ctx->recv.end - ctx->recv.last;
}
size = c->recv(c, ctx->recv.last, n);
#if (NGX_DEBUG)
{
ngx_err_t err;
err = (size >= 0) ? 0 : ngx_socket_errno;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,
"http check recv size: %z, peer: %V ",
size, &peer->check_peer_addr->name);
}
#endif
if (size > 0) {
ctx->recv.last += size;
continue;
} else if (size == 0 || size == NGX_AGAIN) {
break;
} else {
c->error = 1;
goto check_recv_fail;
}
}
rc = peer->parse(peer);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME
"http check parse rc: %i, peer: %V ",
rc, &peer->check_peer_addr->name);
switch (rc) {
case NGX_AGAIN:
/* The peer has closed its half side of the connection. */
if (size == 0) {
ngx_http_upstream_check_status_update(peer, 0);
c->error = 1;
break;
}
return;
case NGX_ERROR:
ngx_log_error(NGX_LOG_ERR, event->log, 0, MODULE_NAME
"check protocol %V error with peer: %V ",
&peer->conf->check_type_conf->name,
&peer->check_peer_addr->name);
ngx_http_upstream_check_status_update(peer, 0);
break;
case NGX_OK:
/* fall through */
default:
ngx_http_upstream_check_status_update(peer, 1);
break;
}
peer->state = NGX_HTTP_CHECK_RECV_DONE;
ngx_http_upstream_check_clean_event(peer);
return;
check_recv_fail:
ngx_http_upstream_check_status_update(peer, 0);
ngx_http_upstream_check_clean_event(peer);
}
static ngx_int_t
ngx_http_upstream_check_http_init(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ctx = peer->check_data;
ucscf = peer->conf;
ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
ctx->recv.start = ctx->recv.pos = NULL;
ctx->recv.end = ctx->recv.last = NULL;
ctx->state = 0;
ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));
return NGX_OK;
}
ngx_int_t
ngx_http_upstream_check_http_parse(ngx_upstream_check_peer_t *peer)
{
ngx_int_t rc;
ngx_uint_t code, code_n;
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ucscf = peer->conf;
ctx = peer->check_data;
if ((ctx->recv.last - ctx->recv.pos) > 0) {
rc = ngx_upstream_check_http_parse_status_line(&ctx->recv,
&ctx->state,
&ctx->status);
if (rc == NGX_AGAIN) {
return rc;
}
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"http parse status line error with peer: %V ",
&peer->check_peer_addr->name);
return rc;
}
code = ctx->status.code;
if (code >= 200 && code < 300) {
code_n = NGX_CHECK_HTTP_2XX;
} else if (code >= 300 && code < 400) {
code_n = NGX_CHECK_HTTP_3XX;
} else if (code >= 400 && code < 500) {
peer->pc.connection->error = 1;
code_n = NGX_CHECK_HTTP_4XX;
} else if (code >= 500 && code < 600) {
peer->pc.connection->error = 1;
code_n = NGX_CHECK_HTTP_5XX;
} else {
peer->pc.connection->error = 1;
code_n = NGX_CHECK_HTTP_ERR;
}
// find content length from [Content-Length: (\d+)\r]
*ctx->status.end = '\0';
u_char * content_len_start = (u_char *)ngx_strstr(ctx->status.start, "Content-Length:");
u_char * content_len_end = NULL;
ngx_uint_t found_body_len, body_len = 0;
if (content_len_start != NULL) {
content_len_end = ngx_strlchr(content_len_start, ctx->status.end, '\r');
if (content_len_end != NULL) {
found_body_len = 1;
ngx_str_t content_len_line = {content_len_end-content_len_start, content_len_start};
body_len = ngx_atoi(content_len_line.data + sizeof("Content-Length: ")-1,
content_len_line.len - (sizeof("Content-Length: ")-1));
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: found content-length: [%V],value: %ui",
&content_len_line, body_len);
}
}
ngx_str_t response_body = {ctx->recv.last - ctx->recv.pos, ctx->recv.pos};
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: "
"code: %ui, expect code mask: %ui. "
"received body len: %ui, content:[%V]",
code, ucscf->code.status_alive,
response_body.len, &response_body);
if (!(code_n & ucscf->code.status_alive)) {
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: code not matched");
return NGX_ERROR;
} else if (ucscf->expect_body_regex != NGX_CONF_UNSET_PTR) {
if (found_body_len == 1 && response_body.len < body_len) {
return NGX_AGAIN;
}
/* body check */
ngx_int_t n;
n = ngx_regex_exec(ucscf->expect_body_regex, &response_body, NULL, 0);
if (n == NGX_REGEX_NO_MATCHED) {
if (found_body_len == 1) {
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: body not matched");
return NGX_ERROR;
} else {
/* we don't known the end flag, so we have to wait next match util timeout */
return NGX_AGAIN;
}
} else {
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: body matched");
return NGX_OK;
}
} else {
ngx_log_debug(LOG_LEVEL, ngx_cycle->log, 0, MODULE_NAME
"[http-response-parse]: code matched");
return NGX_OK;
}
} else {
return NGX_AGAIN;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_fastcgi_process_record(
ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b, ngx_http_status_t *status)
{
u_char ch, *p;
ngx_http_fastcgi_state_e state;
state = ctx->state;
for (p = b->pos; p < b->last; p++) {
ch = *p;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"http fastcgi record byte: %02Xd", ch);
switch (state) {
case ngx_http_fastcgi_st_version:
if (ch != 1) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"upstream sent unsupported FastCGI "
"protocol version: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_type;
break;
case ngx_http_fastcgi_st_type:
switch (ch) {
case NGX_HTTP_FASTCGI_STDOUT:
case NGX_HTTP_FASTCGI_STDERR:
case NGX_HTTP_FASTCGI_END_REQUEST:
status->code = (ngx_uint_t) ch;
break;
default:
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"upstream sent invalid FastCGI "
"record type: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_request_id_hi;
break;
/* we support the single request per connection */
case ngx_http_fastcgi_st_request_id_hi:
if (ch != 0) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"upstream sent unexpected FastCGI "
"request id high byte: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_request_id_lo;
break;
case ngx_http_fastcgi_st_request_id_lo:
if (ch != 1) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"upstream sent unexpected FastCGI "
"request id low byte: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_content_length_hi;
break;
case ngx_http_fastcgi_st_content_length_hi:
ctx->length = ch << 8;
state = ngx_http_fastcgi_st_content_length_lo;
break;
case ngx_http_fastcgi_st_content_length_lo:
ctx->length |= (size_t) ch;
state = ngx_http_fastcgi_st_padding_length;
break;
case ngx_http_fastcgi_st_padding_length:
ctx->padding = (size_t) ch;
state = ngx_http_fastcgi_st_reserved;
break;
case ngx_http_fastcgi_st_reserved:
state = ngx_http_fastcgi_st_data;
b->pos = p + 1;
ctx->state = state;
return NGX_OK;
/* suppress warning */
case ngx_http_fastcgi_st_data:
case ngx_http_fastcgi_st_padding:
break;
}
}
ctx->state = state;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_upstream_check_fastcgi_parse(ngx_upstream_check_peer_t *peer)
{
ngx_int_t rc;
ngx_flag_t done;
ngx_uint_t type, code, code_n;
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ucscf = peer->conf;
ctx = peer->check_data;
if ((ctx->recv.last - ctx->recv.pos) <= 0) {
return NGX_AGAIN;
}
done = 0;
for ( ;; ) {
if (ctx->state < ngx_http_fastcgi_st_data) {
rc = ngx_http_upstream_check_fastcgi_process_record(ctx,
&ctx->recv, &ctx->status);
type = ctx->status.code;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"fastcgi_parse rc: [%i], type: [%ui]", rc, type);
if (rc == NGX_AGAIN) {
return rc;
}
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"check fastcgi parse status line error with peer: %V",
&peer->check_peer_addr->name);
return rc;
}
if (type != NGX_HTTP_FASTCGI_STDOUT
&& type != NGX_HTTP_FASTCGI_STDERR)
{
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"check fastcgi sent unexpected FastCGI record: %d", type);
return NGX_ERROR;
}
if (type == NGX_HTTP_FASTCGI_STDOUT && ctx->length == 0) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"check fastcgi prematurely closed FastCGI stdout");
return NGX_ERROR;
}
}
if (ctx->state == ngx_http_fastcgi_st_padding) {
if (ctx->recv.pos + ctx->padding < ctx->recv.last) {
ctx->status.code = ngx_http_fastcgi_st_version;
ctx->recv.pos += ctx->padding;
continue;
}
if (ctx->recv.pos + ctx->padding == ctx->recv.last) {
ctx->status.code = ngx_http_fastcgi_st_version;
ctx->recv.pos = ctx->recv.last;
return NGX_AGAIN;
}
ctx->padding -= ctx->recv.last - ctx->recv.pos;
ctx->recv.pos = ctx->recv.last;
return NGX_AGAIN;
}
if (ctx->status.code == NGX_HTTP_FASTCGI_STDERR) {
ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, MODULE_NAME
"fastcgi check error");
return NGX_ERROR;
}
/* ctx->status.code == NGX_HTTP_FASTCGI_STDOUT */
if (ctx->recv.pos + ctx->length < ctx->recv.last) {
ctx->recv.last = ctx->recv.pos + ctx->length;
} else {
return NGX_ERROR;
}
ctx->status.code = 0;
for ( ;; ) {
rc = ngx_http_upstream_check_parse_fastcgi_status(ctx,
&ctx->recv,
&ctx->status);
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, MODULE_NAME
"fastcgi http parse status line rc: %i ", rc);
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"fastcgi http parse status line error with peer: %V ",
&peer->check_peer_addr->name);
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_DONE) {
done = 1;
ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, MODULE_NAME
"fastcgi http parse status: %i",
ctx->status.code);
break;
}
/* rc = NGX_OK */
}
if (ucscf->code.status_alive == 0 || done == 0) {
return NGX_OK;
}
code = ctx->status.code;
if (code >= 200 && code < 300) {
code_n = NGX_CHECK_HTTP_2XX;
} else if (code >= 300 && code < 400) {
code_n = NGX_CHECK_HTTP_3XX;
} else if (code >= 400 && code < 500) {
code_n = NGX_CHECK_HTTP_4XX;
} else if (code >= 500 && code < 600) {
code_n = NGX_CHECK_HTTP_5XX;
} else {
code_n = NGX_CHECK_HTTP_ERR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"fastcgi http_parse: code_n: %ui, conf: %ui",
code_n, ucscf->code.status_alive);
if (code_n & ucscf->code.status_alive) {
return NGX_OK;
} else {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_parse_fastcgi_status(ngx_http_upstream_check_ctx_t *ctx,
ngx_buf_t *b, ngx_http_status_t *status)
{
u_char c, ch, *p, *name_s, *name_e;
ngx_flag_t find;
enum {
sw_start = 0,
sw_name,
sw_space_before_value,
sw_value,
sw_space_after_value,
sw_ignore_line,
sw_almost_done,
sw_header_almost_done
} state;
/* the last '\0' is not needed because string is zero terminated */
static u_char lowcase[] =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
"\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
"\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
status->count = 0;
status->code = 0;
find = 0;
name_s = name_e = NULL;
state = sw_start;
for (p = b->pos; p < b->last; p++) {
ch = *p;
switch (state) {
/* first char */
case sw_start:
switch (ch) {
case CR:
state = sw_header_almost_done;
break;
case LF:
goto header_done;
default:
state = sw_name;
c = lowcase[ch];
if (c) {
name_s = p;
break;
}
if (ch == '\0') {
return NGX_ERROR;
}
break;
}
break;
/* header name */
case sw_name:
c = lowcase[ch];
if (c) {
break;
}
if (ch == ':') {
name_e = p;
#if (NGX_DEBUG)
ngx_str_t name;
name.data = name_s;
name.len = name_e - name_s;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"fastcgi header: %V", &name);
#endif
state = sw_space_before_value;
if (ngx_strncasecmp(name_s, (u_char *) "status",
name_e - name_s)
== 0)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"find status header");
find = 1;
}
break;
}
if (ch == CR) {
state = sw_almost_done;
break;
}
if (ch == LF) {
goto done;
}
/* IIS may send the duplicate "HTTP/1.1 ..." lines */
if (ch == '\0') {
return NGX_ERROR;
}
break;
/* space* before header value */
case sw_space_before_value:
switch (ch) {
case ' ':
break;
case CR:
state = sw_almost_done;
break;
case LF:
goto done;
case '\0':
return NGX_ERROR;
default:
state = sw_value;
if (find) {
if (ch < '1' || ch > '9') {
return NGX_ERROR;
}
status->code = status->code * 10 + ch - '0';
if (status->count++ != 0) {
return NGX_ERROR;
}
}
break;
}
break;
/* header value */
case sw_value:
if (find) {
if (ch < '0' || ch > '9') {
return NGX_ERROR;
}
status->code = status->code * 10 + ch - '0';
if (++status->count == 3) {
return NGX_DONE;
}
}
switch (ch) {
case ' ':
state = sw_space_after_value;
break;
case CR:
state = sw_almost_done;
break;
case LF:
goto done;
case '\0':
return NGX_ERROR;
}
break;
/* space* before end of header line */
case sw_space_after_value:
switch (ch) {
case ' ':
break;
case CR:
state = sw_almost_done;
break;
case LF:
state = sw_start;
break;
case '\0':
return NGX_ERROR;
default:
state = sw_value;
break;
}
break;
/* ignore header line */
case sw_ignore_line:
switch (ch) {
case LF:
state = sw_start;
break;
default:
break;
}
break;
/* end of header line */
case sw_almost_done:
switch (ch) {
case LF:
goto done;
case CR:
break;
default:
return NGX_ERROR;
}
break;
/* end of header */
case sw_header_almost_done:
switch (ch) {
case LF:
goto header_done;
default:
return NGX_ERROR;
}
}
}
b->pos = p;
ctx->state = state;
return NGX_AGAIN;
done:
b->pos = p + 1;
ctx->state = sw_start;
return NGX_OK;
header_done:
b->pos = p + 1;
ctx->state = sw_start;
return NGX_OK;
}
static void
ngx_http_upstream_check_http_reinit(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
ctx->send.pos = ctx->send.start;
ctx->send.last = ctx->send.end;
ctx->recv.pos = ctx->recv.last = ctx->recv.start;
ctx->state = 0;
ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));
}
static ngx_int_t
ngx_http_upstream_check_ssl_hello_init(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ctx = peer->check_data;
ucscf = peer->conf;
ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
ctx->recv.start = ctx->recv.pos = NULL;
ctx->recv.end = ctx->recv.last = NULL;
return NGX_OK;
}
/* a rough check of server ssl_hello responses */
static ngx_int_t
ngx_http_upstream_check_ssl_hello_parse(ngx_upstream_check_peer_t *peer)
{
size_t size;
ngx_ssl_server_hello_t *resp;
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
size = ctx->recv.last - ctx->recv.pos;
if (size < sizeof(ngx_ssl_server_hello_t)) {
return NGX_AGAIN;
}
resp = (ngx_ssl_server_hello_t *) ctx->recv.pos;
ngx_log_debug7(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"http check ssl_parse, type: %ud, version: %ud.%ud, "
"length: %ud, handshanke_type: %ud, hello_version: %ud.%ud",
resp->msg_type, resp->version.major, resp->version.minor,
ntohs(resp->length), resp->handshake_type,
resp->hello_version.major, resp->hello_version.minor);
if (resp->msg_type != NGX_SSL_HANDSHAKE) {
return NGX_ERROR;
}
if (resp->handshake_type != NGX_SSL_SERVER_HELLO) {
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_upstream_check_ssl_hello_reinit(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
ctx->send.pos = ctx->send.start;
ctx->send.last = ctx->send.end;
ctx->recv.pos = ctx->recv.last = ctx->recv.start;
}
static ngx_int_t
ngx_http_upstream_check_mysql_init(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ctx = peer->check_data;
ucscf = peer->conf;
ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
ctx->recv.start = ctx->recv.pos = NULL;
ctx->recv.end = ctx->recv.last = NULL;
return NGX_OK;
}
/* a rough check of mysql greeting responses */
static ngx_int_t
ngx_http_upstream_check_mysql_parse(ngx_upstream_check_peer_t *peer)
{
size_t size;
ngx_mysql_handshake_init_t *handshake;
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
size = ctx->recv.last - ctx->recv.pos;
if (size < sizeof(ngx_mysql_handshake_init_t)) {
return NGX_AGAIN;
}
handshake = (ngx_mysql_handshake_init_t *) ctx->recv.pos;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"mysql_parse: packet_number=%ud, protocol=%ud, server=%s",
handshake->packet_number, handshake->protocol_version,
handshake->others);
/* The mysql greeting packet's serial number always begins with 0. */
if (handshake->packet_number != 0x00) {
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_upstream_check_mysql_reinit(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
ctx->send.pos = ctx->send.start;
ctx->send.last = ctx->send.end;
ctx->recv.pos = ctx->recv.last = ctx->recv.start;
}
static ngx_int_t
ngx_http_upstream_check_ajp_init(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ngx_upstream_check_srv_conf_t *ucscf;
ctx = peer->check_data;
ucscf = peer->conf;
ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
ctx->recv.start = ctx->recv.pos = NULL;
ctx->recv.end = ctx->recv.last = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_ajp_parse(ngx_upstream_check_peer_t *peer)
{
size_t size;
u_char *p;
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
size = ctx->recv.last - ctx->recv.pos;
if (size < sizeof(ngx_ajp_cpong_packet)) {
return NGX_AGAIN;
}
p = ctx->recv.pos;
#if (NGX_DEBUG)
{
ngx_ajp_raw_packet_t *ajp;
ajp = (ngx_ajp_raw_packet_t *) p;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, MODULE_NAME
"ajp_parse: preamble=0x%uxd, length=0x%uxd, type=0x%uxd",
ntohs(ajp->preamble), ntohs(ajp->length), ajp->type);
}
#endif
if (ngx_memcmp(ngx_ajp_cpong_packet, p, sizeof(ngx_ajp_cpong_packet)) == 0)
{
return NGX_OK;
} else {
return NGX_ERROR;
}
}
static void
ngx_http_upstream_check_ajp_reinit(ngx_upstream_check_peer_t *peer)
{
ngx_http_upstream_check_ctx_t *ctx;
ctx = peer->check_data;
ctx->send.pos = ctx->send.start;
ctx->send.last = ctx->send.end;
ctx->recv.pos = ctx->recv.last = ctx->recv.start;
}
static void
ngx_http_upstream_check_status_update(ngx_upstream_check_peer_t *peer,
ngx_int_t result)
{
ngx_upstream_check_srv_conf_t *ucscf;
ucscf = peer->conf;
if (result) {
peer->shm->rise_count++;
peer->shm->fall_count = 0;
if (peer->shm->down && peer->shm->rise_count >= ucscf->rise_count) {
peer->shm->down = 0;
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"enable check peer: %V ",
&peer->check_peer_addr->name);
}
} else {
peer->shm->rise_count = 0;
peer->shm->fall_count++;
if (!peer->shm->down && peer->shm->fall_count >= ucscf->fall_count) {
peer->shm->down = 1;
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, MODULE_NAME
"disable check peer: %V ",
&peer->check_peer_addr->name);
}
}
peer->shm->access_time = ngx_current_msec;
}
static void
ngx_http_upstream_check_clean_event(ngx_upstream_check_peer_t *peer)
{
ngx_connection_t *c;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_check_conf_t *cf;
c = peer->pc.connection;
ucscf = peer->conf;
cf = ucscf->check_type_conf;
if (c) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, MODULE_NAME
"http check clean event: index:%i, fd: %d",
peer->index, c->fd);
if (c->error == 0 &&
cf->need_keepalive &&
(c->requests < ucscf->check_keepalive_requests))
{
c->write->handler = ngx_http_upstream_check_dummy_handler;
c->read->handler = ngx_http_upstream_check_discard_handler;
} else {
ngx_close_connection(c);
peer->pc.connection = NULL;
}
}
if (peer->check_timeout_ev.timer_set) {
ngx_del_timer(&peer->check_timeout_ev);
}
peer->state = NGX_HTTP_CHECK_ALL_DONE;
if (peer->check_data != NULL && peer->reinit) {
peer->reinit(peer);
}
peer->shm->owner = NGX_INVALID_PID;
}
static void
ngx_http_upstream_check_timeout_handler(ngx_event_t *event)
{
ngx_upstream_check_peer_t *peer;
if (ngx_http_upstream_check_need_exit()) {
return;
}
peer = event->data;
peer->pc.connection->error = 1;
ngx_log_error(NGX_LOG_ERR, event->log, 0, MODULE_NAME
"check time out with peer: %V ",
&peer->check_peer_addr->name);
ngx_http_upstream_check_status_update(peer, 0);
ngx_http_upstream_check_clean_event(peer);
}
static void
ngx_http_upstream_check_finish_handler(ngx_event_t *event)
{
if (ngx_http_upstream_check_need_exit()) {
return;
}
}
static ngx_int_t
ngx_http_upstream_check_need_exit()
{
if (ngx_terminate || ngx_exiting || ngx_quit) {
ngx_http_upstream_check_clear_all_events();
return 1;
}
return 0;
}
static void
ngx_http_upstream_check_clear_all_events()
{
ngx_uint_t i;
ngx_connection_t *c;
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_peers_t *peers;
static ngx_flag_t has_cleared = 0;
if (has_cleared || http_peers_ctx == NULL) {
return;
}
ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, MODULE_NAME
"clear all the events on %P ", ngx_pid);
has_cleared = 1;
peers = http_peers_ctx;
peer = peers->peers.elts;
for (i = 0; i < peers->peers.nelts; i++) {
if (peer[i].check_ev.timer_set) {
ngx_del_timer(&peer[i].check_ev);
}
if (peer[i].check_timeout_ev.timer_set) {
ngx_del_timer(&peer[i].check_timeout_ev);
}
c = peer[i].pc.connection;
if (c) {
ngx_close_connection(c);
peer[i].pc.connection = NULL;
}
if (peer[i].pool != NULL) {
ngx_destroy_pool(peer[i].pool);
peer[i].pool = NULL;
}
}
}
static ngx_int_t
ngx_http_upstream_check_status_handler(ngx_http_request_t *r)
{
size_t buffer_size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
ngx_upstream_check_peers_t *peers;
ngx_http_upstream_check_loc_conf_t *uclcf;
ngx_http_upstream_check_status_ctx_t *ctx;
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
return NGX_HTTP_NOT_ALLOWED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
uclcf = ngx_http_get_module_loc_conf(r, ngx_http_upstream_check_module);
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_check_status_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_upstream_check_status_parse_args(r, ctx);
if (ctx->format == NULL) {
ctx->format = uclcf->format;
}
r->headers_out.content_type = ctx->format->content_type;
if (r->method == NGX_HTTP_HEAD) {
r->headers_out.status = NGX_HTTP_OK;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
}
peers = http_peers_ctx;
if (peers == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, MODULE_NAME
"http upstream check module can not find any check "
"server, make sure you've added the check servers");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* 1/4 pagesize for each record */
buffer_size = peers->peers.nelts * ngx_pagesize / 4;
buffer_size = ngx_align(buffer_size, ngx_pagesize) + ngx_pagesize;
b = ngx_create_temp_buf(r->pool, buffer_size);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
out.buf = b;
out.next = NULL;
ctx->format->output(b, peers, ctx->flag);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = b->last - b->pos;
if (r->headers_out.content_length_n == 0) {
r->header_only = 1;
}
b->last_buf = 1;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
return ngx_http_output_filter(r, &out);
}
static void
ngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,
ngx_http_upstream_check_status_ctx_t *ctx)
{
ngx_str_t value;
ngx_uint_t i;
ngx_check_status_command_t *command;
if (r->args.len == 0) {
return;
}
for (i = 0; /* void */ ; i++) {
command = &ngx_check_status_commands[i];
if (command->name.len == 0) {
break;
}
if (ngx_http_arg(r, command->name.data, command->name.len, &value)
== NGX_OK) {
if (command->handler(ctx, &value) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, MODULE_NAME
"http upstream check, bad argument: \"%V\"",
&value);
}
}
}
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, MODULE_NAME
"http upstream check, flag: \"%ui\"", ctx->flag);
}
static ngx_int_t
ngx_http_upstream_check_status_command_format(
ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)
{
ctx->format = ngx_http_get_check_status_format_conf(value);
if (ctx->format == NULL) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_status_command_status(
ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)
{
if (value->len == (sizeof("down") - 1)
&& ngx_strncasecmp(value->data, (u_char *) "down", value->len) == 0) {
ctx->flag |= NGX_CHECK_STATUS_DOWN;
} else if (value->len == (sizeof("up") - 1)
&& ngx_strncasecmp(value->data, (u_char *) "up", value->len)
== 0) {
ctx->flag |= NGX_CHECK_STATUS_UP;
} else {
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_upstream_check_status_html_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag)
{
ngx_uint_t i, count;
ngx_upstream_check_peer_t *peer;
peer = peers->peers.elts;
count = 0;
for (i = 0; i < peers->peers.nelts; i++) {
if (flag & NGX_CHECK_STATUS_DOWN) {
if (!peer[i].shm->down) {
continue;
}
} else if (flag & NGX_CHECK_STATUS_UP) {
if (peer[i].shm->down) {
continue;
}
}
count++;
}
b->last = ngx_snprintf(b->last, b->end - b->last,
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\n"
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
"<head>\n"
" <title>Nginx http upstream check status</title>\n"
"</head>\n"
"<body>\n"
"<h1>Nginx http upstream check status</h1>\n"
"<h2>Check upstream server number: %ui, generation: %ui</h2>\n"
"<table style=\"background-color:white\" cellspacing=\"0\" "
" cellpadding=\"3\" border=\"1\">\n"
" <tr bgcolor=\"#C0C0C0\">\n"
" <th>Index</th>\n"
" <th>Upstream</th>\n"
" <th>Name</th>\n"
" <th>Status</th>\n"
" <th>Rise counts</th>\n"
" <th>Fall counts</th>\n"
" <th>Check type</th>\n"
" <th>Check port</th>\n"
" </tr>\n",
count, ngx_http_upstream_check_shm_generation);
for (i = 0; i < peers->peers.nelts; i++) {
if (flag & NGX_CHECK_STATUS_DOWN) {
if (!peer[i].shm->down) {
continue;
}
} else if (flag & NGX_CHECK_STATUS_UP) {
if (peer[i].shm->down) {
continue;
}
}
b->last = ngx_snprintf(b->last, b->end - b->last,
" <tr%s>\n"
" <td>%ui</td>\n"
" <td>%V</td>\n"
" <td>%V</td>\n"
" <td>%s</td>\n"
" <td>%ui</td>\n"
" <td>%ui</td>\n"
" <td>%V</td>\n"
" <td>%ui</td>\n"
" </tr>\n",
peer[i].shm->down ? " bgcolor=\"#FF0000\"" : "",
i,
peer[i].upstream_name,
&peer[i].peer_addr->name,
peer[i].shm->down ? "down" : "up",
peer[i].shm->rise_count,
peer[i].shm->fall_count,
&peer[i].conf->check_type_conf->name,
peer[i].conf->port);
}
b->last = ngx_snprintf(b->last, b->end - b->last,
"</table>\n"
"</body>\n"
"</html>\n");
}
static void
ngx_http_upstream_check_status_csv_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag)
{
ngx_uint_t i;
ngx_upstream_check_peer_t *peer;
peer = peers->peers.elts;
for (i = 0; i < peers->peers.nelts; i++) {
if (flag & NGX_CHECK_STATUS_DOWN) {
if (!peer[i].shm->down) {
continue;
}
} else if (flag & NGX_CHECK_STATUS_UP) {
if (peer[i].shm->down) {
continue;
}
}
b->last = ngx_snprintf(b->last, b->end - b->last,
"%ui,%V,%V,%s,%ui,%ui,%V,%ui\n",
i,
peer[i].upstream_name,
&peer[i].peer_addr->name,
peer[i].shm->down ? "down" : "up",
peer[i].shm->rise_count,
peer[i].shm->fall_count,
&peer[i].conf->check_type_conf->name,
peer[i].conf->port);
}
}
static void
ngx_http_upstream_check_status_json_format(ngx_buf_t *b,
ngx_upstream_check_peers_t *peers, ngx_uint_t flag)
{
ngx_uint_t count, i, last;
ngx_upstream_check_peer_t *peer;
peer = peers->peers.elts;
count = 0;
for (i = 0; i < peers->peers.nelts; i++) {
if (flag & NGX_CHECK_STATUS_DOWN) {
if (!peer[i].shm->down) {
continue;
}
} else if (flag & NGX_CHECK_STATUS_UP) {
if (peer[i].shm->down) {
continue;
}
}
count++;
}
b->last = ngx_snprintf(b->last, b->end - b->last,
"{\"servers\": {\n"
" \"total\": %ui,\n"
" \"generation\": %ui,\n"
" \"server\": [\n",
count,
ngx_http_upstream_check_shm_generation);
last = peers->peers.nelts - 1;
for (i = 0; i < peers->peers.nelts; i++) {
if (flag & NGX_CHECK_STATUS_DOWN) {
if (!peer[i].shm->down) {
continue;
}
} else if (flag & NGX_CHECK_STATUS_UP) {
if (peer[i].shm->down) {
continue;
}
}
b->last = ngx_snprintf(b->last, b->end - b->last,
" {\"index\": %ui, "
"\"upstream\": \"%V\", "
"\"name\": \"%V\", "
"\"status\": \"%s\", "
"\"rise\": %ui, "
"\"fall\": %ui, "
"\"type\": \"%V\", "
"\"port\": %ui}"
"%s\n",
i,
peer[i].upstream_name,
&peer[i].peer_addr->name,
peer[i].shm->down ? "down" : "up",
peer[i].shm->rise_count,
peer[i].shm->fall_count,
&peer[i].conf->check_type_conf->name,
peer[i].conf->port,
(i == last) ? "" : ",");
}
b->last = ngx_snprintf(b->last, b->end - b->last,
" ]\n");
b->last = ngx_snprintf(b->last, b->end - b->last,
"}}\n");
}
static ngx_check_conf_t *
ngx_http_get_check_type_conf(ngx_str_t *str)
{
ngx_uint_t i;
for (i = 0; /* void */ ; i++) {
if (ngx_check_types[i].type == 0) {
break;
}
if (str->len != ngx_check_types[i].name.len) {
continue;
}
if (ngx_strncmp(str->data, ngx_check_types[i].name.data,
str->len) == 0)
{
return &ngx_check_types[i];
}
}
return NULL;
}
static char *
ngx_http_upstream_check(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value, s;
ngx_uint_t i, port, rise, fall, default_down;
ngx_msec_t interval, timeout;
ngx_upstream_check_srv_conf_t *ucscf;
/* default values */
port = 0;
rise = 2;
fall = 5;
interval = 30000;
timeout = 1000;
default_down = 1;
value = cf->args->elts;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
if (ucscf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "type=", 5) == 0) {
s.len = value[i].len - 5;
s.data = value[i].data + 5;
ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);
if (ucscf->check_type_conf == NULL) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "port=", 5) == 0) {
s.len = value[i].len - 5;
s.data = value[i].data + 5;
port = ngx_atoi(s.data, s.len);
if (port == (ngx_uint_t) NGX_ERROR || port == 0) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "interval=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
interval = ngx_atoi(s.data, s.len);
if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "timeout=", 8) == 0) {
s.len = value[i].len - 8;
s.data = value[i].data + 8;
timeout = ngx_atoi(s.data, s.len);
if (timeout == (ngx_msec_t) NGX_ERROR || timeout == 0) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "rise=", 5) == 0) {
s.len = value[i].len - 5;
s.data = value[i].data + 5;
rise = ngx_atoi(s.data, s.len);
if (rise == (ngx_uint_t) NGX_ERROR || rise == 0) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "fall=", 5) == 0) {
s.len = value[i].len - 5;
s.data = value[i].data + 5;
fall = ngx_atoi(s.data, s.len);
if (fall == (ngx_uint_t) NGX_ERROR || fall == 0) {
goto invalid_check_parameter;
}
continue;
}
if (ngx_strncmp(value[i].data, "default_down=", 13) == 0) {
s.len = value[i].len - 13;
s.data = value[i].data + 13;
if (ngx_strcasecmp(s.data, (u_char *) "true") == 0) {
default_down = 1;
} else if (ngx_strcasecmp(s.data, (u_char *) "false") == 0) {
default_down = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%s\", "
"it must be \"true\" or \"false\"",
value[i].data);
return NGX_CONF_ERROR;
}
continue;
}
goto invalid_check_parameter;
}
ucscf->port = port;
ucscf->check_interval = interval;
ucscf->check_timeout = timeout;
ucscf->fall_count = fall;
ucscf->rise_count = rise;
ucscf->default_down = default_down;
if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {
ngx_str_set(&s, "tcp");
ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);
}
return NGX_CONF_OK;
invalid_check_parameter:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
static char *
ngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_uint_t requests;
value = cf->args->elts;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
requests = ngx_atoi(value[1].data, value[1].len);
if (requests == (ngx_uint_t) NGX_ERROR || requests == 0) {
return "invalid value";
}
ucscf->check_keepalive_requests = requests;
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_http_send(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value;
ngx_upstream_check_srv_conf_t *ucscf;
value = cf->args->elts;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
ucscf->send = value[1];
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value, *k, *v;
ngx_upstream_check_srv_conf_t *ucscf;
value = cf->args->elts;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
k = ngx_array_push(ucscf->fastcgi_params);
if (k == NULL) {
return NGX_CONF_ERROR;
}
v = ngx_array_push(ucscf->fastcgi_params);
if (v == NULL) {
return NGX_CONF_ERROR;
}
*k = value[1];
*v = value[2];
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value;
ngx_uint_t bit, i, m;
ngx_conf_bitmask_t *mask;
ngx_upstream_check_srv_conf_t *ucscf;
value = cf->args->elts;
mask = ngx_check_http_expect_alive_masks;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
bit = ucscf->code.status_alive;
for (i = 1; i < cf->args->nelts; i++) {
for (m = 0; mask[m].name.len != 0; m++) {
if (mask[m].name.len != value[i].len
|| ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
{
continue;
}
if (bit & mask[m].mask) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate value \"%s\"", value[i].data);
} else {
bit |= mask[m].mask;
}
break;
}
if (mask[m].name.len == 0) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"invalid value \"%s\"", value[i].data);
return NGX_CONF_ERROR;
}
}
ucscf->code.status_alive = bit;
return NGX_CONF_OK;
}
static char *
ngx_upstream_check_http_body(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_upstream_check_srv_conf_t *ucscf;
u_char *mod;
size_t len;
ngx_str_t *value, *name;
value = cf->args->elts;
ucscf = ngx_http_conf_get_module_srv_conf(cf,
ngx_http_upstream_check_module);
if (cf->args->nelts != 3) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"2 args required.");
return NGX_CONF_ERROR;
}
len = value[1].len;
mod = value[1].data;
name = &value[2];
if (len == 1 && mod[0] == '~') {
if (ngx_upstream_check_http_body_regex(cf, ucscf, name, 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
if (ngx_upstream_check_http_body_regex(cf, ucscf, name, 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid mode args \"%V\", must be ~ or ~* ", &value[1]);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value;
ngx_http_upstream_check_main_conf_t *ucmcf;
ucmcf = ngx_http_conf_get_module_main_conf(cf,
ngx_http_upstream_check_module);
if (ucmcf->check_shm_size) {
return "is duplicate";
}
value = cf->args->elts;
ucmcf->check_shm_size = ngx_parse_size(&value[1]);
if (ucmcf->check_shm_size == (size_t) NGX_ERROR) {
return "invalid value";
}
return NGX_CONF_OK;
}
static ngx_check_status_conf_t *
ngx_http_get_check_status_format_conf(ngx_str_t *str)
{
ngx_uint_t i;
for (i = 0; /* void */ ; i++) {
if (ngx_check_status_formats[i].format.len == 0) {
break;
}
if (str->len != ngx_check_status_formats[i].format.len) {
continue;
}
if (ngx_strncmp(str->data, ngx_check_status_formats[i].format.data,
str->len) == 0)
{
return &ngx_check_status_formats[i];
}
}
return NULL;
}
static char *
ngx_http_upstream_check_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value;
ngx_http_core_loc_conf_t *clcf;
ngx_http_upstream_check_loc_conf_t *uclcf;
value = cf->args->elts;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_upstream_check_status_handler;
if (cf->args->nelts == 2) {
uclcf = ngx_http_conf_get_module_loc_conf(cf,
ngx_http_upstream_check_module);
uclcf->format = ngx_http_get_check_status_format_conf(&value[1]);
if (uclcf->format == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid check format \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static void *
ngx_http_upstream_check_create_main_conf(ngx_conf_t *cf)
{
ngx_http_upstream_check_main_conf_t *ucmcf;
ucmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_main_conf_t));
if (ucmcf == NULL) {
return NULL;
}
ucmcf->peers = ngx_pcalloc(cf->pool,
sizeof(ngx_upstream_check_peers_t));
if (ucmcf->peers == NULL) {
return NULL;
}
ucmcf->peers->checksum = 0;
if (ngx_array_init(&ucmcf->peers->peers, cf->pool, 16,
sizeof(ngx_upstream_check_peer_t)) != NGX_OK)
{
return NULL;
}
return ucmcf;
}
static ngx_buf_t *
ngx_http_upstream_check_create_fastcgi_request(ngx_pool_t *pool,
ngx_str_t *params, ngx_uint_t num)
{
size_t size, len, padding;
ngx_buf_t *b;
ngx_str_t *k, *v;
ngx_uint_t i, j;
ngx_http_fastcgi_header_t *h;
len = 0;
for (i = 0, j = 0; i < num; i++, j = i * 2) {
k = &params[j];
v = &params[j + 1];
len += 1 + k->len + ((v->len > 127) ? 4 : 1) + v->len;
}
padding = 8 - len % 8;
padding = (padding == 8) ? 0 : padding;
size = sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t)
+ sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+ len + padding
+ sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+ sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
b = ngx_create_temp_buf(pool, size);
if (b == NULL) {
return NULL;
}
ngx_http_fastcgi_request_start.br.flags = 0;
ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
sizeof(ngx_http_fastcgi_request_start_t));
h = (ngx_http_fastcgi_header_t *)
(b->pos + sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t));
h->content_length_hi = (u_char) ((len >> 8) & 0xff);
h->content_length_lo = (u_char) (len & 0xff);
h->padding_length = (u_char) padding;
h->reserved = 0;
b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t)
+ sizeof(ngx_http_fastcgi_header_t);
for (i = 0, j = 0; i < num; i++, j = i * 2) {
k = &params[j];
v = &params[j + 1];
if (k->len > 127) {
*b->last++ = (u_char) (((k->len >> 24) & 0x7f) | 0x80);
*b->last++ = (u_char) ((k->len >> 16) & 0xff);
*b->last++ = (u_char) ((k->len >> 8) & 0xff);
*b->last++ = (u_char) (k->len & 0xff);
} else {
*b->last++ = (u_char) k->len;
}
if (v->len > 127) {
*b->last++ = (u_char) (((v->len >> 24) & 0x7f) | 0x80);
*b->last++ = (u_char) ((v->len >> 16) & 0xff);
*b->last++ = (u_char) ((v->len >> 8) & 0xff);
*b->last++ = (u_char) (v->len & 0xff);
} else {
*b->last++ = (u_char) v->len;
}
b->last = ngx_copy(b->last, k->data, k->len);
b->last = ngx_copy(b->last, v->data, v->len);
}
if (padding) {
ngx_memzero(b->last, padding);
b->last += padding;
}
h = (ngx_http_fastcgi_header_t *) b->last;
b->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_PARAMS;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = 0;
h->content_length_lo = 0;
h->padding_length = 0;
h->reserved = 0;
h = (ngx_http_fastcgi_header_t *) b->last;
b->last += sizeof(ngx_http_fastcgi_header_t);
return b;
}
static char *
ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_buf_t *b;
ngx_uint_t i;
ngx_http_upstream_srv_conf_t **uscfp;
ngx_http_upstream_main_conf_t *umcf;
umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
b = ngx_http_upstream_check_create_fastcgi_request(cf->pool,
fastcgi_default_params,
sizeof(fastcgi_default_params) / sizeof(ngx_str_t) / 2);
if (b == NULL) {
return NGX_CONF_ERROR;
}
fastcgi_default_request.data = b->pos;
fastcgi_default_request.len = b->last - b->pos;
uscfp = umcf->upstreams.elts;
for (i = 0; i < umcf->upstreams.nelts; i++) {
if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i]) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
return ngx_http_upstream_check_init_shm(cf, conf);
}
static void *
ngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf)
{
ngx_upstream_check_srv_conf_t *ucscf;
ucscf = ngx_pcalloc(cf->pool, sizeof(ngx_upstream_check_srv_conf_t));
if (ucscf == NULL) {
return NULL;
}
ucscf->fastcgi_params = ngx_array_create(cf->pool, 2 * 4, sizeof(ngx_str_t));
if (ucscf->fastcgi_params == NULL) {
return NULL;
}
ucscf->port = NGX_CONF_UNSET_UINT;
ucscf->fall_count = NGX_CONF_UNSET_UINT;
ucscf->rise_count = NGX_CONF_UNSET_UINT;
ucscf->check_timeout = NGX_CONF_UNSET_MSEC;
ucscf->check_keepalive_requests = NGX_CONF_UNSET_UINT;
ucscf->check_type_conf = NGX_CONF_UNSET_PTR;
ucscf->expect_body_regex = NGX_CONF_UNSET_PTR;
return ucscf;
}
static void *
ngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_upstream_check_loc_conf_t *uclcf;
uclcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_loc_conf_t));
if (uclcf == NULL) {
return NULL;
}
uclcf->format = NGX_CONF_UNSET_PTR;
return uclcf;
}
static char *
ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf)
{
ngx_str_t s;
ngx_buf_t *b;
ngx_check_conf_t *check;
ngx_http_upstream_srv_conf_t *us = conf;
ngx_upstream_check_srv_conf_t *ucscf;
if (us->srv_conf == NULL) {
return NGX_CONF_OK;
}
ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);
if (ucscf->port == NGX_CONF_UNSET_UINT) {
ucscf->port = 0;
}
if (ucscf->fall_count == NGX_CONF_UNSET_UINT) {
ucscf->fall_count = 2;
}
if (ucscf->rise_count == NGX_CONF_UNSET_UINT) {
ucscf->rise_count = 5;
}
if (ucscf->check_interval == NGX_CONF_UNSET_MSEC) {
ucscf->check_interval = 0;
}
if (ucscf->check_timeout == NGX_CONF_UNSET_MSEC) {
ucscf->check_timeout = 1000;
}
if (ucscf->check_keepalive_requests == NGX_CONF_UNSET_UINT) {
ucscf->check_keepalive_requests = 1;
}
if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {
ucscf->check_type_conf = NULL;
}
check = ucscf->check_type_conf;
if (check) {
if (ucscf->send.len == 0) {
ngx_str_set(&s, "fastcgi");
if (check == ngx_http_get_check_type_conf(&s)) {
if (ucscf->fastcgi_params->nelts == 0) {
ucscf->send.data = fastcgi_default_request.data;
ucscf->send.len = fastcgi_default_request.len;
} else {
b = ngx_http_upstream_check_create_fastcgi_request(
cf->pool, ucscf->fastcgi_params->elts,
ucscf->fastcgi_params->nelts / 2);
if (b == NULL) {
return NGX_CONF_ERROR;
}
ucscf->send.data = b->pos;
ucscf->send.len = b->last - b->pos;
}
} else {
ucscf->send.data = check->default_send.data;
ucscf->send.len = check->default_send.len;
}
}
if (ucscf->code.status_alive == 0) {
ucscf->code.status_alive = check->default_status_alive;
}
}
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child)
{
ngx_str_t format = ngx_string("html");
ngx_http_upstream_check_loc_conf_t *prev = parent;
ngx_http_upstream_check_loc_conf_t *conf = child;
ngx_conf_merge_ptr_value(conf->format, prev->format,
ngx_http_get_check_status_format_conf(&format));
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf)
{
ngx_str_t *shm_name;
ngx_uint_t shm_size;
ngx_shm_zone_t *shm_zone;
ngx_http_upstream_check_main_conf_t *ucmcf = conf;
//if (ucmcf->peers->peers.nelts > 0) {
if (1) {
ngx_http_upstream_check_shm_generation++;
shm_name = &ucmcf->peers->check_shm_name;
ngx_http_upstream_check_get_shm_name(shm_name, cf->pool,
ngx_http_upstream_check_shm_generation);
/* The default check shared memory size is 1M */
shm_size = 1 * 1024 * 1024;
shm_size = shm_size < ucmcf->check_shm_size ?
ucmcf->check_shm_size : shm_size;
shm_zone = ngx_shared_memory_add(cf, shm_name, shm_size,
&ngx_http_upstream_check_module);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, MODULE_NAME
"http upstream check, upsteam:%V, shm_zone size:%ui",
shm_name, shm_size);
shm_zone->data = cf->pool;
http_peers_ctx = ucmcf->peers;
shm_zone->init = ngx_http_upstream_check_init_shm_zone;
}
else {
http_peers_ctx = NULL;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name, ngx_pool_t *pool,
ngx_uint_t generation)
{
u_char *last;
shm_name->data = ngx_palloc(pool, SHM_NAME_LEN);
if (shm_name->data == NULL) {
return NGX_ERROR;
}
last = ngx_snprintf(shm_name->data, SHM_NAME_LEN, "%s#%ui",
"ngx_http_upstream_check", generation);
shm_name->len = last - shm_name->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)
{
size_t size;
ngx_str_t oshm_name;
ngx_int_t rc;
ngx_uint_t i, same, number;
ngx_pool_t *pool;
ngx_shm_zone_t *oshm_zone;
ngx_slab_pool_t *shpool;
ngx_upstream_check_peer_t *peer;
ngx_upstream_check_peers_t *peers;
ngx_upstream_check_srv_conf_t *ucscf;
ngx_upstream_check_peer_shm_t *peer_shm, *opeer_shm;
ngx_upstream_check_peers_shm_t *peers_shm, *opeers_shm;
opeers_shm = NULL;
peers_shm = NULL;
ngx_str_null(&oshm_name);
same = 0;
peers = http_peers_ctx;
if (peers == NULL) {
return NGX_OK;
}
number = peers->peers.nelts;
pool = shm_zone->data;
if (pool == NULL) {
pool = ngx_cycle->pool;
}
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
// alloc peers_shm
size = sizeof(*peers_shm) +
(number ) * sizeof(ngx_upstream_check_peer_shm_t);//last item not use :)
peers_shm = ngx_slab_alloc(shpool, size);
if (peers_shm == NULL) {
goto failure;
}
ngx_memzero(peers_shm, size);
peers_shm->generation = ngx_http_upstream_check_shm_generation;
peers_shm->checksum = peers->checksum;
peers_shm->number = number;
// end
ngx_log_error(NGX_LOG_INFO, shm_zone->shm.log, 0, MODULE_NAME
"[ngx-healthcheck][http] old data: %p ",data);
if (data) {
opeers_shm = data;
if ((opeers_shm->number == number)
&& (opeers_shm->checksum == peers->checksum)) {
peers_shm = data;
same = 1;
}
}
if (!same) {
if (ngx_http_upstream_check_shm_generation > 1) {
ngx_http_upstream_check_get_shm_name(&oshm_name,
pool, ngx_http_upstream_check_shm_generation - 1);
/* The global variable ngx_cycle still points to the old one */
oshm_zone = ngx_shared_memory_find((ngx_cycle_t *) ngx_cycle,
&oshm_name,
&ngx_http_upstream_check_module);
if (oshm_zone) {
opeers_shm = oshm_zone->data;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0, MODULE_NAME
"http upstream check, find oshm_zone:%p, "
"opeers_shm: %p",
oshm_zone, opeers_shm);
}
}
}
peer = peers->peers.elts;
for (i = 0; i < number; i++) {
peer_shm = &peers_shm->peers[i];
/*
* This function may be triggered before the old stale
* work process exits. The owner may stick to the old
* pid.
*/
peer_shm->owner = NGX_INVALID_PID;
if (same) {
continue;
}
peer_shm->socklen = peer[i].peer_addr->socklen;
peer_shm->sockaddr = ngx_slab_alloc(shpool, peer_shm->socklen);
if (peer_shm->sockaddr == NULL) {
goto failure;
}
ngx_memcpy(peer_shm->sockaddr, peer[i].peer_addr->sockaddr,
peer_shm->socklen);
if (opeers_shm) {
//todo:bug
opeer_shm = ngx_http_upstream_check_find_shm_peer(opeers_shm,
peer[i].peer_addr);
if (opeer_shm) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0, MODULE_NAME
"http upstream check, inherit opeer: %V ",
&peer[i].peer_addr->name);
rc = ngx_http_upstream_check_init_shm_peer(peer_shm, opeer_shm,
0, pool, &peer[i].peer_addr->name);
if (rc != NGX_OK) {
return NGX_ERROR;
}
continue;
}
}
ucscf = peer[i].conf;
rc = ngx_http_upstream_check_init_shm_peer(peer_shm, NULL,
ucscf->default_down, pool,
&peer[i].peer_addr->name);
if (rc != NGX_OK) {
return NGX_ERROR;
}
}
peers->peers_shm = peers_shm;
shm_zone->data = peers_shm;
return NGX_OK;
failure:
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, MODULE_NAME
"http upstream check_shm_size is too small, "
"you should specify a larger size.");
return NGX_ERROR;
}
static ngx_shm_zone_t *
ngx_shared_memory_find(ngx_cycle_t *cycle, ngx_str_t *name, void *tag)
{
ngx_uint_t i;
ngx_shm_zone_t *shm_zone;
ngx_list_part_t *part;
part = (ngx_list_part_t *) &(cycle->shared_memory.part);
shm_zone = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
shm_zone = part->elts;
i = 0;
}
if (name->len != shm_zone[i].shm.name.len) {
continue;
}
if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) != 0)
{
continue;
}
if (tag != shm_zone[i].tag) {
continue;
}
return &shm_zone[i];
}
return NULL;
}
static ngx_upstream_check_peer_shm_t *
ngx_http_upstream_check_find_shm_peer(ngx_upstream_check_peers_shm_t *p,
ngx_addr_t *addr)
{
ngx_uint_t i;
ngx_upstream_check_peer_shm_t *peer_shm;
for (i = 0; i < p->number; i++) {
peer_shm = &p->peers[i];
if (addr->socklen != peer_shm->socklen) {
continue;
}
//zcx:todo: segv
if (ngx_memcmp(addr->sockaddr, peer_shm->sockaddr, addr->socklen) == 0) {
return peer_shm;
}
}
return NULL;
}
static ngx_int_t
ngx_http_upstream_check_init_shm_peer(ngx_upstream_check_peer_shm_t *psh,
ngx_upstream_check_peer_shm_t *opsh, ngx_uint_t init_down,
ngx_pool_t *pool, ngx_str_t *name)
{
u_char *file;
if (opsh) {
psh->access_time = opsh->access_time;
psh->access_count = opsh->access_count;
psh->fall_count = opsh->fall_count;
psh->rise_count = opsh->rise_count;
psh->busyness = opsh->busyness;
psh->down = opsh->down;
} else {
psh->access_time = 0;
psh->access_count = 0;
psh->fall_count = 0;
psh->rise_count = 0;
psh->busyness = 0;
psh->down = init_down;
}
#if (NGX_HAVE_ATOMIC_OPS)
file = NULL;
#else
file = ngx_pnalloc(pool, ngx_cycle->lock_file.len + name->len);
if (file == NULL) {
return NGX_ERROR;
}
(void) ngx_sprintf(file, "%V%V%Z", &ngx_cycle->lock_file, name);
#endif
#if (nginx_version >= 1002000)
if (ngx_shmtx_create(&psh->mutex, &psh->lock, file) != NGX_OK) {
#else
if (ngx_shmtx_create(&psh->mutex, (void *) &psh->lock, file) != NGX_OK) {
#endif
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_check_init_process(ngx_cycle_t *cycle)
{
ngx_http_upstream_check_main_conf_t *ucmcf;
ucmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_check_module);
if (ucmcf == NULL) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, MODULE_NAME
"[ngx-healthcheck][http][init-process] no http section, skip init");
return NGX_OK;
}
return ngx_http_upstream_check_add_timers(cycle);
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/hrq/ngx_healthcheck_module.git
git@gitee.com:hrq/ngx_healthcheck_module.git
hrq
ngx_healthcheck_module
ngx_healthcheck_module
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891