Implement Expect: 100-continue
This commit is contained in:
		@@ -387,6 +387,14 @@ int http_send_request(sock *server, http_req *req) {
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int http_send_100_continue(sock *client) {
 | 
				
			||||||
 | 
					    char buf[256];
 | 
				
			||||||
 | 
					    char date_buf[64];
 | 
				
			||||||
 | 
					    int size = sprintf(buf, "HTTP/1.1 100 Continue\r\nDate: %s\r\nServer: " SERVER_STR "\r\n\r\n",
 | 
				
			||||||
 | 
					                       http_get_date(date_buf, sizeof(date_buf)));
 | 
				
			||||||
 | 
					    return sock_send_x(client, buf, size, 0) == -1 ? -1 : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const http_status *http_get_status(status_code_t status_code) {
 | 
					const http_status *http_get_status(status_code_t status_code) {
 | 
				
			||||||
    for (int i = 0; i < http_statuses_size; i++) {
 | 
					    for (int i = 0; i < http_statuses_size; i++) {
 | 
				
			||||||
        if (http_statuses[i].code == status_code) {
 | 
					        if (http_statuses[i].code == status_code) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,6 +174,8 @@ int http_send_response(sock *client, http_res *res);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int http_send_request(sock *server, http_req *req);
 | 
					int http_send_request(sock *server, http_req *req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int http_send_100_continue(sock *client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const http_status *http_get_status(status_code_t status_code);
 | 
					const http_status *http_get_status(status_code_t status_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const http_status_msg *http_get_error_msg(status_code_t status_code);
 | 
					const http_status_msg *http_get_error_msg(status_code_t status_code);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										153
									
								
								src/lib/proxy.c
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								src/lib/proxy.c
									
									
									
									
									
								
							@@ -440,35 +440,74 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
 | 
					    const char *client_expect = http_get_header_field(&req->hdr, "Expect");
 | 
				
			||||||
    unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0;
 | 
					    int expect_100_continue = (client_expect != NULL && strcasecmp(client_expect, "100-continue") == 0);
 | 
				
			||||||
    const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
 | 
					    int ignore_content = 0;
 | 
				
			||||||
 | 
					    if (expect_100_continue) {
 | 
				
			||||||
    ret = 0;
 | 
					        http_res tmp_res = {
 | 
				
			||||||
    if (content_len > 0) {
 | 
					            .version = "1.1",
 | 
				
			||||||
        ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len);
 | 
					            .status = http_get_status(501),
 | 
				
			||||||
    } else if (strcontains(transfer_encoding, "chunked")) {
 | 
					        };
 | 
				
			||||||
        ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED);
 | 
					        if (http_init_hdr(&tmp_res.hdr) != 0) {
 | 
				
			||||||
    }
 | 
					            res->status = http_get_status(500);
 | 
				
			||||||
 | 
					            ctx->origin = INTERNAL;
 | 
				
			||||||
    if (ret < 0 || (content_len != 0 && ret != content_len)) {
 | 
					            error("Unable to initialize http header");
 | 
				
			||||||
        if (ret == -1 && errno != EPROTO) {
 | 
					            return -1;
 | 
				
			||||||
            res->status = http_get_status(502);
 | 
					        }
 | 
				
			||||||
            ctx->origin = SERVER_REQ;
 | 
					
 | 
				
			||||||
            error("Unable to send request to server (2)");
 | 
					        ret = proxy_peek_response(proxy, &tmp_res, ctx, custom_status, err_msg);
 | 
				
			||||||
            sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
 | 
					        if (ret < 0)
 | 
				
			||||||
            return -1;
 | 
					            return (int) ret;
 | 
				
			||||||
        } else if (ret == -1) {
 | 
					
 | 
				
			||||||
            res->status = http_get_status(400);
 | 
					        if (tmp_res.status->code == 100) {
 | 
				
			||||||
            ctx->origin = CLIENT_REQ;
 | 
					            if (sock_recv_x(&proxy->proxy, buffer, ret, 0) == -1) {
 | 
				
			||||||
            error("Unable to receive request from client");
 | 
					                res->status = http_get_status(502);
 | 
				
			||||||
            sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
 | 
					                ctx->origin = SERVER_RES;
 | 
				
			||||||
 | 
					                error("Unable to receive from server");
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (http_send_response(client, &tmp_res) != 0) {
 | 
				
			||||||
 | 
					                res->status = http_get_status(400);
 | 
				
			||||||
 | 
					                ctx->origin = CLIENT_RES;
 | 
				
			||||||
 | 
					                error("Unable to send to client");
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ignore_content = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!ignore_content) {
 | 
				
			||||||
 | 
					        const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
 | 
				
			||||||
 | 
					        unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0;
 | 
				
			||||||
 | 
					        const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret = 0;
 | 
				
			||||||
 | 
					        if (content_len > 0) {
 | 
				
			||||||
 | 
					            ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len);
 | 
				
			||||||
 | 
					        } else if (strcontains(transfer_encoding, "chunked")) {
 | 
				
			||||||
 | 
					            ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (ret < 0 || (content_len != 0 && ret != content_len)) {
 | 
				
			||||||
 | 
					            if (ret == -1 && errno != EPROTO) {
 | 
				
			||||||
 | 
					                res->status = http_get_status(502);
 | 
				
			||||||
 | 
					                ctx->origin = SERVER_REQ;
 | 
				
			||||||
 | 
					                error("Unable to send request to server (2)");
 | 
				
			||||||
 | 
					                sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            } else if (ret == -1) {
 | 
				
			||||||
 | 
					                res->status = http_get_status(400);
 | 
				
			||||||
 | 
					                ctx->origin = CLIENT_REQ;
 | 
				
			||||||
 | 
					                error("Unable to receive request from client");
 | 
				
			||||||
 | 
					                sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            res->status = http_get_status(500);
 | 
				
			||||||
 | 
					            ctx->origin = INTERNAL;
 | 
				
			||||||
 | 
					            error("Unknown Error");
 | 
				
			||||||
            return -1;
 | 
					            return -1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        res->status = http_get_status(500);
 | 
					 | 
				
			||||||
        ctx->origin = INTERNAL;
 | 
					 | 
				
			||||||
        error("Unknown Error");
 | 
					 | 
				
			||||||
        return -1;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (sock_set_socket_timeout(&proxy->proxy, SERVER_SOCKET_TIMEOUT_RES) != 0) {
 | 
					    if (sock_set_socket_timeout(&proxy->proxy, SERVER_SOCKET_TIMEOUT_RES) != 0) {
 | 
				
			||||||
@@ -478,6 +517,48 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
 | 
				
			|||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        ret = proxy_peek_response(proxy, res, ctx, custom_status, err_msg);
 | 
				
			||||||
 | 
					        if (ret < 0) {
 | 
				
			||||||
 | 
					            return (int) ret;
 | 
				
			||||||
 | 
					        } else if (sock_recv_x(&proxy->proxy, buffer, ret, 0) == -1) {
 | 
				
			||||||
 | 
					            res->status = http_get_status(502);
 | 
				
			||||||
 | 
					            ctx->origin = SERVER_RES;
 | 
				
			||||||
 | 
					            error("Unable to receive from server");
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (res->status->code == 100) {
 | 
				
			||||||
 | 
					            if (http_send_response(client, res) != 0) {
 | 
				
			||||||
 | 
					                res->status = http_get_status(400);
 | 
				
			||||||
 | 
					                ctx->origin = CLIENT_RES;
 | 
				
			||||||
 | 
					                error("Unable to send to client");
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    long keep_alive_timeout = http_get_keep_alive_timeout(&res->hdr);
 | 
				
			||||||
 | 
					    proxy->http_timeout = (keep_alive_timeout > 0) ? keep_alive_timeout * 1000000 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connection = http_get_header_field(&res->hdr, "Connection");
 | 
				
			||||||
 | 
					    proxy->close = !streq(res->version, "1.1") || strcontains(connection, "close") || strcontains(connection, "Close");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = proxy_response_header(req, res, conf);
 | 
				
			||||||
 | 
					    if (ret != 0) {
 | 
				
			||||||
 | 
					        res->status = http_get_status(500);
 | 
				
			||||||
 | 
					        ctx->origin = INTERNAL;
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int proxy_peek_response(proxy_ctx_t *proxy, http_res *res, http_status_ctx *ctx, http_status *custom_status, char *err_msg) {
 | 
				
			||||||
 | 
					    char buffer[CHUNK_SIZE], err_buf[256];
 | 
				
			||||||
 | 
					    long ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK);
 | 
					    ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK);
 | 
				
			||||||
    if (ret <= 0) {
 | 
					    if (ret <= 0) {
 | 
				
			||||||
        int e_sys = error_get_sys(), e_ssl = error_get_ssl();
 | 
					        int e_sys = error_get_sys(), e_ssl = error_get_ssl();
 | 
				
			||||||
@@ -569,23 +650,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        ptr = pos0 + 2;
 | 
					        ptr = pos0 + 2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (sock_recv_x(&proxy->proxy, buffer, header_len, 0) == -1)
 | 
					    return header_len;
 | 
				
			||||||
        return -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    long keep_alive_timeout = http_get_keep_alive_timeout(&res->hdr);
 | 
					 | 
				
			||||||
    proxy->http_timeout = (keep_alive_timeout > 0) ? keep_alive_timeout * 1000000 : 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    connection = http_get_header_field(&res->hdr, "Connection");
 | 
					 | 
				
			||||||
    proxy->close = !streq(res->version, "1.1") || strcontains(connection, "close") || strcontains(connection, "Close");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ret = proxy_response_header(req, res, conf);
 | 
					 | 
				
			||||||
    if (ret != 0) {
 | 
					 | 
				
			||||||
        res->status = http_get_status(500);
 | 
					 | 
				
			||||||
        ctx->origin = INTERNAL;
 | 
					 | 
				
			||||||
        return -1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) {
 | 
					int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,8 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int proxy_init(proxy_ctx_t **proxy, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg);
 | 
					int proxy_init(proxy_ctx_t **proxy, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int proxy_peek_response(proxy_ctx_t *proxy, http_res *res, http_status_ctx *ctx, http_status *custom_status, char *err_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags);
 | 
					int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int proxy_dump(proxy_ctx_t *proxy, char *buf, long len);
 | 
					int proxy_dump(proxy_ctx_t *proxy, char *buf, long len);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,15 +211,8 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    long ret;
 | 
					    long ret;
 | 
				
			||||||
    if (s->enc) {
 | 
					    if (s->enc) {
 | 
				
			||||||
        while (1) {
 | 
					        ret = SSL_write(s->ssl, buf, (int) len);
 | 
				
			||||||
            ret = SSL_write(s->ssl, buf, (int) len);
 | 
					        if (ret <= 0) sock_error(s, (int) ret);
 | 
				
			||||||
            if (ret <= 0) {
 | 
					 | 
				
			||||||
                sock_error(s, (int) ret);
 | 
					 | 
				
			||||||
                if (error_get_ssl() == SSL_ERROR_WANT_WRITE)
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else if (s->pipe) {
 | 
					    } else if (s->pipe) {
 | 
				
			||||||
        if (flags & ~MSG_MORE) {
 | 
					        if (flags & ~MSG_MORE) {
 | 
				
			||||||
            errno = EINVAL;
 | 
					            errno = EINVAL;
 | 
				
			||||||
@@ -262,15 +255,8 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
 | 
				
			|||||||
    long ret;
 | 
					    long ret;
 | 
				
			||||||
    if (s->enc) {
 | 
					    if (s->enc) {
 | 
				
			||||||
        int (*func)(SSL *, void *, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read;
 | 
					        int (*func)(SSL *, void *, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read;
 | 
				
			||||||
        while (1) {
 | 
					        ret = func(s->ssl, buf, (int) len);
 | 
				
			||||||
            ret = func(s->ssl, buf, (int) len);
 | 
					        if (ret <= 0) sock_error(s, (int) ret);
 | 
				
			||||||
            if (ret <= 0)  {
 | 
					 | 
				
			||||||
                sock_error(s, (int) ret);
 | 
					 | 
				
			||||||
                if (error_get_ssl() == SSL_ERROR_WANT_READ)
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else  if (s->pipe) {
 | 
					    } else  if (s->pipe) {
 | 
				
			||||||
        if (flags & ~MSG_WAITALL) {
 | 
					        if (flags & ~MSG_WAITALL) {
 | 
				
			||||||
            errno = EINVAL;
 | 
					            errno = EINVAL;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,13 +77,32 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t **fcgi_cnx) {
 | 
				
			|||||||
    (*fcgi_cnx) = &fcgi_cnx_buf;
 | 
					    (*fcgi_cnx) = &fcgi_cnx_buf;
 | 
				
			||||||
    fastcgi_handle_connection(ctx, fcgi_cnx);
 | 
					    fastcgi_handle_connection(ctx, fcgi_cnx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int expect_100_continue = 0;
 | 
				
			||||||
 | 
					    const char *client_expect = http_get_header_field(&req->hdr, "Expect");
 | 
				
			||||||
 | 
					    if (client_expect != NULL && strcasecmp(client_expect, "100-continue") == 0) {
 | 
				
			||||||
 | 
					        expect_100_continue = 1;
 | 
				
			||||||
 | 
					    } else if (client_expect != NULL) {
 | 
				
			||||||
 | 
					        fastcgi_close_cnx((&fcgi_cnx_buf));
 | 
				
			||||||
 | 
					        res->status = http_get_status(417);
 | 
				
			||||||
 | 
					        return 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    const char *client_content_length = http_get_header_field(&req->hdr, "Content-Length");
 | 
					    const char *client_content_length = http_get_header_field(&req->hdr, "Content-Length");
 | 
				
			||||||
    const char *client_transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
 | 
					    const char *client_transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
 | 
				
			||||||
    if (client_content_length != NULL) {
 | 
					    if (client_content_length != NULL) {
 | 
				
			||||||
 | 
					        if (expect_100_continue) {
 | 
				
			||||||
 | 
					            http_send_100_continue(client);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
 | 
					        unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
 | 
				
			||||||
        ret = fastcgi_receive(*fcgi_cnx, client, client_content_len);
 | 
					        ret = fastcgi_receive(*fcgi_cnx, client, client_content_len);
 | 
				
			||||||
    } else if (strcontains(client_transfer_encoding, "chunked")) {
 | 
					    } else if (strcontains(client_transfer_encoding, "chunked")) {
 | 
				
			||||||
 | 
					        if (expect_100_continue) {
 | 
				
			||||||
 | 
					            http_send_100_continue(client);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        ret = fastcgi_receive_chunked(*fcgi_cnx, client);
 | 
					        ret = fastcgi_receive_chunked(*fcgi_cnx, client);
 | 
				
			||||||
 | 
					    } else if (expect_100_continue) {
 | 
				
			||||||
 | 
					        fastcgi_close_cnx((&fcgi_cnx_buf));
 | 
				
			||||||
 | 
					        res->status = http_get_status(417);
 | 
				
			||||||
 | 
					        return 3;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        ret = 0;
 | 
					        ret = 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,6 +159,12 @@ static int local_handler(client_ctx_t *ctx) {
 | 
				
			|||||||
        return 1;
 | 
					        return 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const char *client_expect = http_get_header_field(&req->hdr, "Expect");
 | 
				
			||||||
 | 
					    if (client_expect != NULL) {
 | 
				
			||||||
 | 
					        res->status = http_get_status(417);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res->status = http_get_status(200);
 | 
					    res->status = http_get_status(200);
 | 
				
			||||||
    cache_init_uri(ctx->conf->cache, uri);
 | 
					    cache_init_uri(ctx->conf->cache, uri);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user