|
|
| version 1.97, 2020/10/14 21:35:00 | version 1.105, 2020/12/07 23:25:20 |
|---|---|
| Line 114 public: | Line 114 public: |
| } | } |
| bool read(int sock, size_t size){ | bool read(int sock, size_t size){ |
| if(length+size>buf_size) | if(length + size > buf_size) |
| resize(buf_size*2 + size); | resize(buf_size * 2 + size); |
| ssize_t received_size=recv(sock, buf + length, size, 0); | ssize_t received_size=recv(sock, buf + length, size, 0); |
| if(received_size==0) | if(received_size == 0) |
| return false; | return false; |
| if(received_size<0) { | if(received_size < 0) { |
| if(int no=pa_socks_errno()) | if(int no = pa_socks_errno()) |
| throw Exception("http.timeout", 0, "error receiving response body: %s (%d)", pa_socks_strerr(no), no); | throw Exception("http.timeout", 0, "error receiving response: %s (%d)", pa_socks_strerr(no), no); |
| return false; | return false; |
| } | } |
| length+=received_size; | length+=received_size; |
| Line 262 int HTTP_response::read_response(int soc | Line 262 int HTTP_response::read_response(int soc |
| #ifdef PA_USE_ALARM | #ifdef PA_USE_ALARM |
| static sigjmp_buf timeout_env; | static sigjmp_buf timeout_env; |
| static void timeout_handler(int /*sig*/){ | static void timeout_handler(int /*sig*/){ |
| siglongjmp(timeout_env, 1); | siglongjmp(timeout_env, 1); |
| } | } |
| #define ALARM(value) alarm(value) | |
| #else | |
| #define ALARM(value) | |
| #endif | #endif |
| static int http_request(HTTP_response& response, const char* host, short port, const char* request, size_t request_size, int timeout_secs, bool fail_on_status_ne_200) { | static int http_request(HTTP_response& response, const char* host, short port, const char* request, size_t request_size, int timeout_secs, bool fail_on_status_ne_200) { |
| if(!host) | if(!host) |
| throw Exception("http.host", 0, "zero hostname"); //never | throw Exception("http.host", 0, "zero hostname"); //never |
| volatile // to prevent makeing it register variable, because it will be clobbered by longjmp [thanks gcc warning] | volatile int sock=-1; // to prevent makeing it register variable, because it will be clobbered by longjmp [thanks gcc warning] |
| int sock=-1; | |
| #ifdef PA_USE_ALARM | |
| signal(SIGALRM, timeout_handler); | |
| #endif | |
| #ifdef PA_USE_ALARM | #ifdef PA_USE_ALARM |
| signal(SIGALRM, timeout_handler); | |
| if(sigsetjmp(timeout_env, 1)) { | if(sigsetjmp(timeout_env, 1)) { |
| // stupid gcc [2.95.4] generated bad code | // duplicating closesocket to make code more simple for old compilers |
| // which failed to handle sigsetjmp+throw: crashed inside of pre-throw code. | if(sock>=0) |
| // rewritten simplier [athough duplicating closesocket code] | closesocket(sock); |
| if(sock>=0) | throw Exception("http.timeout", 0, "timeout occurred while retrieving document"); |
| closesocket(sock); | |
| throw Exception("http.timeout", 0, "timeout occurred while retrieving document"); | |
| return 0; // never | return 0; // never |
| } else { | } else |
| alarm(timeout_secs); | |
| #endif | #endif |
| { | |
| ALARM(timeout_secs); | |
| try { | try { |
| int result; | int result; |
| struct sockaddr_in dest; | struct sockaddr_in dest; |
| Line 325 static int http_request(HTTP_response& r | Line 325 static int http_request(HTTP_response& r |
| result=response.read_response(sock, fail_on_status_ne_200); | result=response.read_response(sock, fail_on_status_ne_200); |
| closesocket(sock); | closesocket(sock); |
| #ifdef PA_USE_ALARM | ALARM(0); |
| alarm(0); | |
| #endif | |
| return result; | return result; |
| } catch(...) { | } catch(...) { |
| #ifdef PA_USE_ALARM | ALARM(0); |
| alarm(0); | |
| #endif | |
| if(sock>=0) | if(sock>=0) |
| closesocket(sock); | closesocket(sock); |
| rethrow; | rethrow; |
| } | } |
| #ifdef PA_USE_ALARM | |
| } | } |
| #endif | |
| } | } |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| Line 364 char *pa_http_safe_header_name(const cha | Line 358 char *pa_http_safe_header_name(const cha |
| return result; | return result; |
| } | } |
| static void http_pass_header(HashStringValue::key_type aname, | static void http_pass_header(HashStringValue::key_type aname, HashStringValue::value_type avalue, Http_pass_header_info *info) { |
| HashStringValue::value_type avalue, | |
| Http_pass_header_info *info) { | |
| const char* name_cstr=aname.cstr(); | const char* name_cstr=aname.cstr(); |
| Line 921 File_read_http_result pa_internal_file_r | Line 913 File_read_http_result pa_internal_file_r |
| /* ********************** httpd *************************** */ | /* ********************** httpd *************************** */ |
| enum EscapeState { | |
| Initial, | |
| Default, | |
| EscapeFirst, | |
| EscapeSecond | |
| }; | |
| static bool check_uri(const char *uri){ | |
| EscapeState state=Initial; | |
| uint escapedValue; | |
| const char *pattern="/../"; | |
| const char *pos=pattern; | |
| while(*uri){ | |
| uchar c=(uchar)*(uri++); | |
| switch(state) { | |
| case Initial: | |
| if(c!='/') | |
| return false; | |
| state=Default; | |
| break; | |
| case Default: | |
| if(c=='%'){ | |
| state=EscapeFirst; | |
| continue; | |
| } | |
| if(c=='?') | |
| return true; | |
| break; | |
| case EscapeFirst: | |
| if(isxdigit(c)){ | |
| state=EscapeSecond; | |
| escapedValue=hex_value[c] << 4; | |
| continue; | |
| } | |
| return false; | |
| case EscapeSecond: | |
| if(isxdigit(c)){ | |
| state=Default; | |
| c=(uchar)(escapedValue + hex_value[c]); | |
| // implementing Apache AllowEncodedSlashes Off just in case | |
| if(c=='/' || c=='\\') | |
| return false; | |
| break; | |
| } | |
| return false; | |
| } | |
| if(c==*pos || c=='\\' && *pos=='/'){ | |
| if(!*(++pos)) | |
| return false; | |
| } else { | |
| pos=pattern; | |
| } | |
| } | |
| return true; | |
| } | |
| class HTTPD_request : public HTTP_response { | class HTTPD_request : public HTTP_response { |
| public: | public: |
| const char *method; | const char *method; |
| Line 928 public: | Line 981 public: |
| HTTPD_request() : HTTP_response(), method(NULL), uri(NULL){}; | HTTPD_request() : HTTP_response(), method(NULL), uri(NULL){}; |
| ssize_t pa_recv(int sockfd, char *buf, size_t len); | |
| bool read(int sock, size_t size){ | |
| if(length + size > buf_size) | |
| resize(buf_size * 2 + size); | |
| ssize_t received_size=pa_recv(sock, buf + length, size); | |
| if(received_size == 0) | |
| return false; | |
| if(received_size < 0) { | |
| if(int no = pa_socks_errno()) | |
| throw Exception("httpd.timeout", 0, "error receiving request: %s (%d)", pa_socks_strerr(no), no); | |
| return false; | |
| } | |
| length+=received_size; | |
| buf[length]='\0'; | |
| return true; | |
| } | |
| const char *extract_method(char *method_line){ | const char *extract_method(char *method_line){ |
| char* uri_start = strchr(method_line, ' '); | char* uri_start = strchr(method_line, ' '); |
| Line 940 public: | Line 1011 public: |
| return NULL; | return NULL; |
| uri=pa_strdup(uri_start+1, uri_end-uri_start-1); | uri=pa_strdup(uri_start+1, uri_end-uri_start-1); |
| if(!check_uri(uri)) | |
| throw Exception("httpd.request", 0, "invalid uri '%s'", uri); | |
| return str_upper(method_line, uri_start-method_line); | return str_upper(method_line, uri_start-method_line); |
| } | } |
| void read_header(int); | void read_header(int); |
| size_t read_post(int, char *, size_t); | size_t read_post(int, char *, size_t); |
| }; | }; |
| Line 952 enum HTTPD_request_state { | Line 1027 enum HTTPD_request_state { |
| HTTPD_HEADERS | HTTPD_HEADERS |
| }; | }; |
| ssize_t HTTPD_request::pa_recv(int sockfd, char *buffer, size_t len){ | |
| #ifdef PA_USE_ALARM | |
| signal(SIGALRM, timeout_handler); | |
| if(sigsetjmp(timeout_env, 1)) { | |
| throw Exception("httpd.timeout", 0, "timeout occurred while receiving request"); | |
| return 0; // never | |
| } else | |
| #endif | |
| { | |
| ALARM(pa_httpd_timeout); | |
| ssize_t result=recv(sockfd, buffer, len, 0); | |
| ALARM(0); | |
| return result; | |
| } | |
| } | |
| void HTTPD_request::read_header(int sock) { | void HTTPD_request::read_header(int sock) { |
| enum HTTPD_request_state state = HTTPD_METHOD; | enum HTTPD_request_state state = HTTPD_METHOD; |
| Line 1001 void HTTPD_request::read_header(int sock | Line 1092 void HTTPD_request::read_header(int sock |
| size_t HTTPD_request::read_post(int sock, char *body, size_t max_bytes) { | size_t HTTPD_request::read_post(int sock, char *body, size_t max_bytes) { |
| size_t total_read = min(length - body_offset, max_bytes); | size_t total_read = min(length - body_offset, max_bytes); |
| memcpy(body, buf, total_read); | memcpy(body, buf + body_offset, total_read); |
| while (total_read < max_bytes){ | while (total_read < max_bytes){ |
| ssize_t received_size = recv(sock, buf + total_read, max_bytes - total_read, 0); | ssize_t received_size = pa_recv(sock, body + total_read, max_bytes - total_read); |
| if(received_size == 0) | if(received_size == 0) |
| return total_read; | return total_read; |
| if(received_size < 0) { | if(received_size < 0) { |
| Line 1110 int HTTPD_Server::bind(const char *host_ | Line 1201 int HTTPD_Server::bind(const char *host_ |
| port = host_port; | port = host_port; |
| } | } |
| if(!set_addr(&me, host, pa_atoui(port))){ | if(!set_addr(&me, host, (short)pa_atoui(port))){ |
| if (host) | if (host) |
| throw Exception("httpd.bind", 0, "can not resolve hostname \"%s\"", host); | throw Exception("httpd.bind", 0, "can not resolve hostname \"%s\"", host); |
| me.sin_addr.s_addr=INADDR_ANY; | me.sin_addr.s_addr=INADDR_ANY; |