소스 코드 측면 에서 nginx 와 uwsgi 의 통신 과정 을 이해 하 다.
상품 사진 을 올 리 는 문제 와 관련 된 항목 이 있 었 는데 저 는 그림 의 크기 를 제한 할 때 전체 그림 을 메모리 에 읽 은 다음 에 크기 를 판단 합 니 다.이런 방법 은 악의 적 인 공격 이나 그림 이 많 을 때 웹 애플 리 케 이 션 의 성능 에 심각 한 영향 을 줄 수 있다.원래 첫 번 째 콘 텐 츠 - length 를 먼저 판단 함으로써 크기 를 제한 하려 고 했 습 니 다.그러나 나중에 그림 이 전단 의 nginx 에서 완전히 읽 은 후에 uwsgi 에 전달 된다 면 이러한 판단 은 nginx 의 성능 에 영향 을 줄 것 이 라 고 생각 했다.이 를 위해 저 는 nginx 의 소스 코드 를 살 펴 보고 nginx 와 uwsgi 의 통신 과정 을 찾 았 습 니 다. 다음은 전체 통신 의 구체 적 인 절차 입 니 다.
리 셋 함수 설정
nginx 가 브 라 우 저 등 을 통 해 보 내 온 요청 을 proxypass 혹은 uwsgi패스 는 상위 웹 애플 리 케 이 션 에 전송 하여 처리 한 결 과 를 브 라 우 저 에 보 냅 니 다.uwsgi_pass 명령 의 처리 함 수 는 ngxhttp_uwsgi_handler, 즉 요청 이 있 으 면 설정 uwsgipass 의 location 시 ngx 호출http_uwsgi_handler 방법, 이 방법 은 전체 uwsgi 사건 처리 의 입구 방법 입 니 다.다음은 이 방법 을 살 펴 보 겠 습 니 다.
static ngx_int_t ngx_http_uwsgi_handler(ngx_http_request_t *r)
{
ngx_http_upstream_t *u;
ngx_http_uwsgi_loc_conf_t *uwcf;
uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
u = r->upstream;
……
u->create_request = ngx_http_uwsgi_create_request;// wsgi
u->process_header = ngx_http_uwsgi_process_status_line;// wsgi uwsgi
……
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);// body
……
}
이 방법 은 우선 ngx 를 만 듭 니 다.http_upstream_t 구조 체, 그리고 이 구조 체 의 몇 가지 반전 함 수 를 설정 합 니 다.nginx 는 고도 로 모듈 화 된 웹 서버 로 모든 기능 이 하나의 모듈 을 통 해 이 루어 지 며 uwsgi 와 의 통신 도 예외 가 아니다.전체 통신 과정 은 ngxhttp_uwsgi_module. c 이 모듈 파일 이 구현 되 었 습 니 다.물론 모든 모듈 을 nginx 에 추가 하려 면 갈고리 가 있어 야 합 니 다. nginx 에서 일부 링크 입 니 다. 그리고 모든 모듈 의 처리 방법 을 이 링크 에 추가 하면 nginx 프레임 코드 에 의 해 하나씩 호출 됩 니 다.웹 애플 리 케 이 션 과 통신 하 는 갈고리 파일 은 ngx 입 니 다.http_upstream. c 파일 은 nginx 가 언제 이 갈고리 함 수 를 촉발 하 는 지 에 대해 쉽게 말 하면 이 갈고리 함수 들 을 epoll 의 읽 기와 쓰기 이벤트 에 할당 하 는 것 입 니 다. 읽 기와 쓰기 요청 이 있 을 때 epoll 의 이벤트 응답 체 제 는 이 갈고리 함 수 를 호출 합 니 다.보시 다시 피 위의 갈고리 함 수 는 구조 요청 체 만 있 고 응답 머리 만 해석 할 수 있 으 며 패키지 에 대한 처리 가 없습니다. 이것 은 nginx 가 패키지 에 대해 처리 할 필요 가 없고 간단 한 저장 전송 만 할 수 있 기 때 문 입 니 다.
브 라 우 저 에서 데이터 읽 기
먼저 nginx 가 브 라 우 저 에서 데 이 터 를 얼마나 읽 어야 할 지 결정 하 는 것 을 보 겠 습 니 다. 이것 은 content - length 의 첫 번 째 부분 에서 결정 되 는 것 이 분명 하지만 client 를 받 을 수 있 습 니 다.max_body_size 명령 의 영향.우 리 는 nginx. conf 에 client 를 추가 할 수 있 습 니 다.max_body_size 설정 명령, 이 명령 은 http, server, location 세 곳 에 놓 을 수 있 습 니 다.nginx 는 머리 를 읽 은 후 src / http / ngx 를 호출 합 니 다.http_core_module. c 파일 중 ngxhttp_core_find_config_phase 방법 은 재 작성 한 URI 에 따라 일치 하 는 location 블록 을 검색 합 니 다. 이 때 설정 파일 은 합 쳐 졌 고 간단 한 검색 일 뿐 입 니 다.클 라 이언 트 가 설 치 된 것 을 발견 하면max_body_size 프로필 은 요청 체 가 주어진 크기 를 초과 하 는 지 아래 비 교 를 통 해 판단 합 니 다.
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
if (clcf->client_max_body_size < r->headers_in.content_length_n)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"client intended to send too large body: %O bytes",
r->headers_in.content_length_n);
}
}
설정 한 크기 를 초과 하지 않 았 다 면, ngxhttp_request_body_t 구조 체 에서 rest 는 content - length 의 크기 로 설정 되 어 읽 어야 할 바이트 수 를 표시 합 니 다.진정한 읽 기 방법 ngxhttp_read_client_request_body, 이 방법 은 반전 함수 ngxhttp_upstream_init, 이 반전 함 수 는 nginx 와 상류 통신 의 입 구 를 여 는 것 입 니 다.주의, 비록 ngxhttp_read_client_request_body 방법 은 막 히 지 않 습 니 다. 즉, 데이터 수신 이 없 을 때 바로 돌아 갑 니 다. 그러나 데이터 가 모두 수신 되 어야 ngx 를 호출 할 수 있 습 니 다.http_upstream_init 방법, 구체 적 인 실현 코드 보기:
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler)
{
if (rb->rest == 0) { /* body */
if (r->request_body_in_file_only) {
ngx_http_write_request_body(r); /* */
}
post_handler(r); /* , ngx_http_upstream_init */
}
r->read_event_handler = ngx_http_read_client_request_body_handler;//
rc = ngx_http_do_read_client_request_body(r);
}
이 를 통 해 알 수 있 듯 이 읽 기 가 끝나 면 ngx 를 호출 합 니 다.http_upstream_init 는 uwsgi 와 통신 을 시작 합 니 다. 없 으 면 먼저 읽 기 이 벤트 를 ngx 로 설정 합 니 다.http_read_client_request_body_handler 방법, 이 방법 내부 에서 오류 검 사 를 하 는 것 외 에 도 ngx 를 호출 합 니 다.http_read_client_request_body 방법, 즉 모든 body 를 읽 지 않 으 면 nginx 는 epoll 에 이 방법 을 계속 호출 하여 body 의 읽 기 작업 을 하 라 고 알려 줍 니 다.이 방법의 마지막 에 ngx 호출http_do_read_client_request_body 방법 으로 진정한 읽 기 동작 을 진행 합 니 다.
static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r)
{
for ( ;; ) {
for ( ;; ) {
if (rb->buf->last == rb->buf->end) { /* request_body buf */
rc = ngx_http_request_body_filter(r, &out);/* , , */
}
n = c->recv(c, rb->buf->last, size); //
if (rb->rest == 0) { //
break;
}
}
}
if (rb->temp_file || r->request_body_in_file_only) {
ngx_http_write_request_body(r); // , ,
}
if (!r->request_body_no_buffering) { //nginx proxy_request_buffering
r->read_event_handler = ngx_http_block_reading;
rb->post_handler(r);// ngx_http_upstream_init
}
return NGX_OK;
}
위 에서 볼 수 있 듯 이 proxy 가 열 리 면request_buffering (기본 오픈) 은 nginx 가 모든 데 이 터 를 읽 은 후에 uwsgi 에 보 냅 니 다.
uwsgi 에 데이터 보 내기
다음은 전체 통신 의 입구 함수 ngxhttp_upstream_init:
void ngx_http_upstream_init(ngx_http_request_t *r)
{
ngx_http_upstream_init_request(r);
}
static void ngx_http_upstream_init_request(ngx_http_request_t *r)
{
u->create_request(r);// ngx_http_uwsgi_create_request
ngx_http_upstream_connect(r, u);// uwsgi
}
재 ngxhttp_uwsgi_create_request 방법 에서 주요 업 무 는 wsgi 프로 토 콜 에 따라 요청 메 시 지 를 구축 하 는 것 이다. 주로 첫 번 째 정보 이 고 구 축 된 연결 을 통 해 uwsgi 에 메 시 지 를 보 내 는 것 이다.방법 ngxhttp_upstream_connect 에 서 는 uwsgi 와 통신 하 는 방법 을 설정 하 였 습 니 다.
static void ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
……
rc = ngx_event_connect_peer(&u->peer); //
u->write_event_handler = ngx_http_upstream_send_request_handler; // uwsgi
u->read_event_handler = ngx_http_upstream_process_header; // uwsgi
……
ngx_http_upstream_send_request(r, u, 1); //
}
uwsgi 에서 머리 정보 읽 기
이로써 알 수 있 듯 이 uwsgi 에서 데 이 터 를 nginx 로 보 낼 때 읽 기 이벤트, 즉 ngx 를 호출 합 니 다http_upstream_process_header 방법:
static void ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
for ( ;; ) {
n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); // uwsgi
if (n == NGX_AGAIN) { // , epoll
ngx_handle_read_event(c->read, 0);
}
rc = u->process_header(r); // uwsgi ngx_http_uwsgi_process_status_line
if (rc == NGX_AGAIN) { // ,
continue;
}
break;
}
/* rc == NGX_OK: */
if (!r->subrequest_in_memory) {
ngx_http_upstream_send_response(r, u);//
return;
}
}
머리 정 보 를 분석 한 후, ngx 를 호출 합 니 다.http_upstream_process_header 의 마지막 ngxhttp_upstream_send_response 방법 은 클 라 이언 트 에 응답 헤드 를 보 냅 니 다.또한 이 방법 에 서 는 사용자 가 uwsgi 를 설정 할 때 uwsgi 를 받 을 지 여부 와 관련 이 있 습 니 다.buffering 로고 (기본 오픈).열 리 면 nginx 는 가능 한 한 많은 데 이 터 를 캐 시 해서 브 라 우 저 에 보 냅 니 다. 메모리 캐 시가 부족 하면 임시 파일 에 저장 합 니 다.사용자 가 닫 히 면 nginx 는 최대 uwsgi 만 캐 시 합 니 다.buffer_size 크기 의 body 는 캐 시 가 가득 찼 을 때 백 엔 드 가 차단 모델 을 기반 으로 한 uwsgi 가 send 할 수 없어 서 막 힐 수 있 습 니 다.다음은 이 두 가지 상황 에서 의 데이터 전송 에 대해 토론 한다.
캐 시 를 열지 않 은 상태 에서 uwsgi 에서 패 키 지 를 읽 고 전송 합 니 다.
uwsgi 에서buffering 이 off 로 설 정 된 경우 uwsgi 에서 body 를 읽 고 body 를 브 라 우 저 에 보 내 는 방법 은 각각 ngx 입 니 다.http_upstream_process_non_buffered_upstream 과 ngxhttp_upstream_process_non_buffered_downstream。이 두 가지 방법 은 내부 에서 모두 ngx 를 호출 했다.http_upstream_process_non_buffered_request 방법, 다음은 이 방법 을 구체 적 으로 살 펴 보 겠 습 니 다.
static void ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,ngx_uint_t do_write)
{
/* , , , , */
for ( ;; )
{
if (do_write)//
{
if (u->out_bufs || u->busy_bufs)
{
/* out_bufs */
rc = ngx_http_output_filter(r, u->out_bufs);
}
//
if (u->busy_bufs == NULL)
{
b->pos = b->start;
b->last = b->start;
}
}
/* do_write 0, */
size = b->end - b->last;// buffer
if (size && upstream->read->ready)
{
n = upstream->recv(upstream, b->last, size);// buffer
if (n == NGX_AGAIN)
{// epoll
break;
}
if (n > 0)
{
u->state->response_length += n;
/* */
u->input_filter(u->input_filter_ctx, n);
}
/* , do_write 1, */
do_write = 1;
continue;
}
break;
}
}
이 방법 은 데 이 터 를 읽 고 do 를 설정 하 는 것 을 볼 수 있 습 니 다.write = 1, 전송 이 벤트 를 촉발 하기 때문에 비 캐 시 입 니 다.
캐 시 를 열 었 을 때 uwsgi 에서 패 키 지 를 읽 고 전송 합 니 다.
uwsgi 에서buffering 이 on 으로 설 정 된 경우 uwsgi 에서 body 를 읽 고 body 를 브 라 우 저 에 보 내 는 방법 은 각각 ngx 입 니 다.http_upstream_process_upstream 과 ngxhttp_upstream_process_downstream。이 두 가지 방법 은 마지막 으로 ngx 를 호출 합 니 다.event_pipe 방법 은 이 방법 에서 표지 위 치 를 통 해 각각 uwsgi 에서 데 이 터 를 읽 고 브 라 우 저 에 데 이 터 를 보 냅 니 다. 구체 적 으로 다음 과 같 습 니 다.
ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
{
for ( ;; )
{
if (do_write)
{
rc = ngx_event_pipe_write_to_downstream(p);//
}
p->read = 0;
p->upstream_blocked = 0;
/* */
ngx_event_pipe_read_upstream(p);
if (!p->read && !p->upstream_blocked) {
break;
}
/* , p->read 1, do_write 1, */
do_write = 1;
}
}
uwsgi 에서 데 이 터 를 읽 습 니 다. 이 과정 에서 다음 과 같은 몇 가지 상황 과 관련 될 수 있 습 니 다. 1) 응답 머리 를 받 을 때 일부 패 킷 을 받 을 수 있 습 니 다.2) bufs. num 상한 선 에 도달 하지 않 으 면 bufs. size 크기 의 메모리 블록 을 수신 버퍼 로 할당 할 수 있 습 니 다.4) 버퍼 가 가득 차 면 임시 파일 을 써 야 합 니 다.
static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
{
for ( ;; )
{
/* ngx_buf_t */
if (p->free_raw_bufs)
{
chain = p->free_raw_bufs;
}
// ,
else if (p->allocated < p->bufs.num)
{
b = ngx_create_temp_buf(p->pool, p->bufs.size);//
……
}
//
else if (p->cacheable|| p->temp_file->offset < p->max_temp_file_size)
{
rc = ngx_event_pipe_write_chain_to_temp_file(p);//
} else {
break;
}
//
n = p->upstream->recv_chain(p->upstream, chain);
}
if (p->length == 0) {// ,
p->upstream_done = 1;
p->read = 1;
}
}
이 방법 은 가능 한 한 많은 데 이 터 를 캐 시 해서 브 라 우 저 에 보 내 는 것 임 을 알 수 있다.
총결산
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Portswigger의 연구실 작성: CSRF 토큰 보호를 사용한 기본 클릭재킹이 견습생 수준 실습에서는 일부 CSRF 토큰 보호가 있음에도 불구하고 클릭재킹에 취약한 웹사이트에서 계정 삭제 흐름을 악용합니다. 주어진 자격 증명으로 로그인하면 계정 페이지로 이동한 후 사용자 계정을 삭제하는 데...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.