Diff for /parser3/src/main/pa_http.C between versions 1.83 and 1.97

version 1.83, 2020/09/30 20:01:08 version 1.97, 2020/10/14 21:35:00
Line 18  volatile const char * IDENT_PA_HTTP_C="$ Line 18  volatile const char * IDENT_PA_HTTP_C="$
   
 #ifdef _MSC_VER  #ifdef _MSC_VER
 #include <windows.h>  #include <windows.h>
   #define socklen_t int
 #else  #else
 #define closesocket close  #define closesocket close
 #endif  #endif
Line 48  volatile const char * IDENT_PA_HTTP_C="$ Line 49  volatile const char * IDENT_PA_HTTP_C="$
   
 // helpers  // helpers
   
 bool ResponseHeaders::add_header(const char *line){  bool HTTP_Headers::add_header(const char *line){
         const char *value=strchr(line, ':');          const char *value=strchr(line, ':');
   
         if(value && value != line){ // we need only headers, not the response code          if(value && value != line){ // we need only headers, not the response code
Line 58  bool ResponseHeaders::add_header(const c Line 59  bool ResponseHeaders::add_header(const c
                         content_type=header.value;                          content_type=header.value;
   
                 if(header.name == String::Body("CONTENT-LENGTH") && content_length==0)                  if(header.name == String::Body("CONTENT-LENGTH") && content_length==0)
                         content_length=pa_atoul(header.value.cstr(), 10);                          ALTER_EXCEPTION_COMMENT(content_length=pa_atoul(header.value.cstr()), " for content-length");
   
                 headers+=header;                  headers+=header;
   
Line 96  static bool set_addr(struct sockaddr_in Line 97  static bool set_addr(struct sockaddr_in
         return false;          return false;
 }  }
   
 class HTTP_response {  class HTTP_response : public PA_Allocated {
 public:  public:
         char *buf;          char *buf;
         size_t length;          size_t length;
         size_t buf_size;          size_t buf_size;
         size_t body_offset;          size_t body_offset;
   
         ResponseHeaders headers;          HTTP_Headers headers;
         String &url;  
   
         HTTP_response(String& aurl) : buf(NULL), length(0), buf_size(0), body_offset(0), url(aurl){}          HTTP_response() : buf(NULL), length(0), buf_size(0), body_offset(0){}
   
         void resize(size_t size){          void resize(size_t size){
                 buf_size=size;                  buf_size=size;
Line 121  public: Line 121  public:
                         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", &url, "error receiving response body: %s (%d)", pa_socks_strerr(no), no);                                  throw Exception("http.timeout", 0, "error receiving response body: %s (%d)", pa_socks_strerr(no), no);
                         return false;                          return false;
                 }                  }
                 length+=received_size;                  length+=received_size;
Line 130  public: Line 130  public:
         }          }
   
         size_t first_line(){          size_t first_line(){
                 char *headers=strchr(buf, '\n');                  char *header=strchr(buf, '\n');
                 if(!headers)                  if(!header)
                         return false;                          return false;
   
                 return headers-buf;                  return header-buf;
         }          }
   
         const char *status_code(char *status_line, int &result){          const char *status_code(char *status_line, int &result){
Line 152  public: Line 152  public:
                         return status_line;                          return status_line;
   
                 const char *result_str=pa_strdup(status_start, status_end-status_start);                  const char *result_str=pa_strdup(status_start, status_end-status_start);
                 result=atoi(result_str);                  ALTER_EXCEPTION_COMMENT(result=pa_atoui(result_str), " for HTTP status");
                 return result_str;                  return result_str;
         }          }
   
Line 185  public: Line 185  public:
                 for(;i.has_next();){                  for(;i.has_next();){
                         const char *line=i.next()->cstr();                          const char *line=i.next()->cstr();
                         if(!headers.add_header(line))                          if(!headers.add_header(line))
                                 throw Exception("http.response", &url, "bad response from host - bad header \"%s\"", line);                                  throw Exception("http.response", 0, "bad response from host - bad header \"%s\"", line);
                 }                  }
         }          }
   
           int read_response(int sock, bool fail_on_status_ne_200);
 };  };
   
 enum HTTP_response_state {  enum HTTP_response_state {
Line 197  enum HTTP_response_state { Line 198  enum HTTP_response_state {
         HTTP_BODY          HTTP_BODY
 };  };
   
 static int http_read_response(HTTP_response& response, int sock, bool fail_on_status_ne_200) {  int HTTP_response::read_response(int sock, bool fail_on_status_ne_200) {
         HTTP_response_state state=HTTP_STATUS_CODE;          HTTP_response_state state=HTTP_STATUS_CODE;
         int result=0;          int result=0;
   
         size_t chunk_size=0x400*16;          size_t chunk_size=0x400*16;
         response.resize(2*chunk_size);          resize(2*chunk_size);
   
         while(response.read(sock, chunk_size)){          while(read(sock, chunk_size)){
                 switch(state){                  switch(state){
                         case HTTP_STATUS_CODE: {                          case HTTP_STATUS_CODE: {
                                 size_t status_size=response.first_line();                                  size_t status_size=first_line();
                                 if(!status_size)                                  if(!status_size)
                                         break;                                          break;
   
                                 const char *status=response.status_code(pa_strdup(response.buf, status_size), result);                                  const char *status=status_code(pa_strdup(buf, status_size), result);
   
                                 if(!result || fail_on_status_ne_200 && result!=200)                                  if(!result || fail_on_status_ne_200 && result!=200)
                                         throw Exception("http.status", status ? new String(status) : &String::Empty, "invalid HTTP response status");                                          throw Exception("http.status", status ? new String(status) : &String::Empty, "invalid HTTP response status");
Line 220  static int http_read_response(HTTP_respo Line 221  static int http_read_response(HTTP_respo
                         }                          }
   
                         case HTTP_HEADERS: {                          case HTTP_HEADERS: {
                                 if(!response.body_start())                                  if(!body_start())
                                         break;                                          break;
   
                                 response.parse_headers();                                  parse_headers();
   
                                 size_t content_length=check_file_size(response.headers.content_length, response.url);                                  size_t content_length=check_file_size(headers.content_length, 0);
                                 if(content_length>0 && (content_length + response.body_offset) > response.length){                                  if(content_length>0 && (content_length + body_offset) > length){
                                         response.resize(content_length + response.body_offset + 0x400*64);                                          resize(content_length + body_offset + 0x400*64);
                                 }                                  }
   
                                 state=HTTP_BODY;                                  state=HTTP_BODY;
Line 242  static int http_read_response(HTTP_respo Line 243  static int http_read_response(HTTP_respo
         }          }
   
         if(state==HTTP_STATUS_CODE)          if(state==HTTP_STATUS_CODE)
                 throw Exception("http.response", &response.url, "bad response from host - no status found (size=%u)", response.length);                  throw Exception("http.response", 0, "bad response from host - no status found (size=%u)", length);
   
         if(state==HTTP_HEADERS){          if(state==HTTP_HEADERS){
                 response.parse_headers();                  parse_headers();
                 response.body_offset=response.length;                  body_offset=length;
         }          }
   
         return result;          return result;
Line 322  static int http_request(HTTP_response& r Line 323  static int http_request(HTTP_response& r
                                 throw Exception("http.timeout", 0, "error sending request: %s (%d)", pa_socks_strerr(no), no);                                  throw Exception("http.timeout", 0, "error sending request: %s (%d)", pa_socks_strerr(no), no);
                         }                          }
   
                         result=http_read_response(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  #ifdef PA_USE_ALARM
                         alarm(0);                          alarm(0);
Line 862  File_read_http_result pa_internal_file_r Line 863  File_read_http_result pa_internal_file_r
         }          }
                   
   
         HTTP_response response(connect_string);          HTTP_response response;
   
         // sending request          // sending request
         int status_code=http_request(response, idna_host, port, request, request_size, timeout_secs, fail_on_status_ne_200);          int status_code;
           ALTER_EXCEPTION_SOURCE(status_code=http_request(response, idna_host, port, request, request_size, timeout_secs, fail_on_status_ne_200), &connect_string);
   
         // processing results          // processing results
         char* raw_body=response.buf + response.body_offset;          char* raw_body=response.buf + response.body_offset;
Line 884  File_read_http_result pa_internal_file_r Line 886  File_read_http_result pa_internal_file_r
         if (!real_remote_charset)          if (!real_remote_charset)
                 real_remote_charset=asked_remote_charset; // never null                  real_remote_charset=asked_remote_charset; // never null
   
         for(Array_iterator<ResponseHeaders::Header> i(response.headers.headers); i.has_next(); ){          for(Array_iterator<HTTP_Headers::Header> i(response.headers.headers); i.has_next(); ){
                 ResponseHeaders::Header header=i.next();                  HTTP_Headers::Header header=i.next();
   
                 header.transcode(*real_remote_charset, r.charsets.source());                  header.transcode(*real_remote_charset, r.charsets.source());
   
Line 896  File_read_http_result pa_internal_file_r Line 898  File_read_http_result pa_internal_file_r
         }          }
   
         // filling $.cookies          // filling $.cookies
         if(Value *vcookies=vtables->hash().get("SET-COOKIE"))          if(vcookies=vtables->hash().get("SET-COOKIE"))
                 result.headers->put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table())));                  result.headers->put(HTTP_COOKIES_NAME, new VTable(parse_cookies(r, vcookies->get_table())));
   
         // output response          // output response
Line 916  File_read_http_result pa_internal_file_r Line 918  File_read_http_result pa_internal_file_r
   
         return result;          return result;
 }  }
   
   /* ********************** httpd *************************** */
   
   class HTTPD_request : public HTTP_response {
   public:
           const char *method;
           const char *uri;
   
           HTTPD_request() : HTTP_response(), method(NULL), uri(NULL){};
   
           const char *extract_method(char *method_line){
                   char* uri_start = strchr(method_line, ' ');
   
                   if(!uri_start || uri_start == method_line)
                           return NULL;
   
                   char* uri_end=strchr(uri_start+1, ' ');
   
                   if(!uri_end || uri_end == uri_start+1)
                           return NULL;
   
                   uri=pa_strdup(uri_start+1, uri_end-uri_start-1);
                   return str_upper(method_line, uri_start-method_line);
           }
   
           void read_header(int);
           size_t read_post(int, char *, size_t);
   };
   
   enum HTTPD_request_state {
           HTTPD_METHOD,
           HTTPD_HEADERS
   };
   
   void HTTPD_request::read_header(int sock) {
           enum HTTPD_request_state state = HTTPD_METHOD;
   
           size_t chunk_size = 0x400*4;
           resize(chunk_size);
   
           while(read(sock, chunk_size)){
                   switch(state){
                           case HTTPD_METHOD: {
                                   size_t method_size = first_line();
                                   if(!method_size)
                                           break;
   
                                   char *method_line = pa_strdup(buf, method_size);
                                   method = extract_method(method_line);
   
                                   if(!method ||
                                           strcmp(method, "GET") &&
                                           strcmp(method, "HEAD") &&
                                           strcmp(method, "POST") &&
                                           strcmp(method, "PUT") &&
                                           strcmp(method, "DELETE") &&
                                           strcmp(method, "PATCH")
                                   )
                                           throw Exception("httpd.method", new String(method ? method : method_line), "invalid request method");
                                   state = HTTPD_HEADERS;
                           }
   
                           case HTTPD_HEADERS: {
                                   if(!body_start())
                                           break;
   
                                   parse_headers();
                                   return;
                           }
                   }
           }
   
           if(state == HTTPD_METHOD)
                   throw Exception("httpd.request", 0, "bad request from host - no method found (size=%u)", length);
   
           if(state == HTTPD_HEADERS){
                   parse_headers();
                   body_offset=length;
           }
   }
   
   size_t HTTPD_request::read_post(int sock, char *body, size_t max_bytes) {
           size_t total_read = min(length - body_offset, max_bytes);
           memcpy(body, buf, total_read);
   
           while (total_read < max_bytes){
                   ssize_t received_size = recv(sock, buf + total_read, max_bytes - total_read, 0);
                   if(received_size == 0)
                           return total_read;
                   if(received_size < 0) {
                           if(int no = pa_socks_errno())
                                   throw Exception("httpd.timeout", new String(uri), "error receiving request body: %s (%d)", pa_socks_strerr(no), no);
                           return total_read;
                   }
                   total_read += received_size;
           }
           return total_read;
   }
   
   /* ********************************************************** */
   
   Array<HTTP_Headers::Header> &HTTPD_Connection::headers() {
           return request->headers.headers;
   }
   
   const char *HTTPD_Connection::method() {
           return request->method;
   }
   
   const char *HTTPD_Connection::uri() {
           return request->uri;
   }
   
   const char *HTTPD_Connection::content_type() {
           return request->headers.content_type.cstr();
   }
   
   uint64_t HTTPD_Connection::content_length(){
           return request->headers.content_length;
   }
   
   void HTTPD_Connection::read_header(){
           request = new HTTPD_request();
           request->read_header(sock);
   }
   
   size_t HTTPD_Connection::read_post(char *body, size_t max_bytes) {
           return request->read_post(sock, body, max_bytes);
   }
   
   size_t HTTPD_Connection::send_body(const void *buf, size_t size) {
           if(send(sock, (const char*)buf, size, 0) != (ssize_t)size) {
                   int no=pa_socks_errno();
                   throw Exception("httpd.timeout", 0, "error sending response: %s (%d)", pa_socks_strerr(no), no);
           }
           return size;
   }
   
   HTTPD_Connection::~HTTPD_Connection(){
           if(sock != -1)
                   closesocket(sock);
   }
   
   static int sock_ready(int fd,int operation,int timeout_value){
           struct timeval timeout = {0, timeout_value * 1000};
           fd_set fds;
           FD_ZERO(&fds);
           FD_SET(fd, &fds);
           switch (operation){
                   case 0: return select(fd + 1, &fds, NULL, NULL, &timeout)>0;  /* read */
                   case 1: return select(fd + 1, NULL, &fds, NULL, &timeout)>0;  /* write */
                   default: return select(fd + 1, &fds, &fds, NULL, &timeout)>0;  /* both */
           }
   }
   
   bool HTTPD_Connection::accept(int server_sock, int timeout_value) {
           int ready = sock_ready(server_sock, 0, timeout_value);
           if (ready < 0) {
                   int no=pa_socks_errno();
                   if(no == EINTR)
                           return false;
                   throw Exception("httpd.accept", 0, "error waiting for connection: %s (%d)", pa_socks_strerr(no), no);
           }
           if (ready == 0)
                   return false; /* Timeout */
   
           struct sockaddr_in addr;
           socklen_t sock_addr_len = sizeof(struct sockaddr_in);
           memset(&addr, 0, sock_addr_len);
   
           sock = ::accept(server_sock, (struct sockaddr *)&addr, &sock_addr_len);
           if(server_sock == -1){
                   int no=pa_socks_errno();
                   throw Exception("httpd.accept", 0, "error accepting connection: %s (%d)", pa_socks_strerr(no), no);
           }
   
           remote_addr = pa_strdup(inet_ntoa(addr.sin_addr));
           return true;
   }
   
   int HTTPD_Server::bind(const char *host_port){
           struct sockaddr_in me;
   
           const char *port = strchr(host_port, ':');
           const char *host = NULL;
           if(port && port > host_port){
                   host = pa_strdup(host_port, port - host_port);
                   port += 1;
           } else {
                   port = host_port;
           }
   
           if(!set_addr(&me, host, pa_atoui(port))){
                   if (host)
                           throw Exception("httpd.bind", 0, "can not resolve hostname \"%s\"", host);
                   me.sin_addr.s_addr=INADDR_ANY;
           }
   
           int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/);
   
           if(sock < 0){
                   int no=pa_socks_errno();
                   throw Exception("httpd.bind", 0, "can not make socket: %s (%d)", pa_socks_strerr(no), no);
           }
   
           static int sock_on = 1;
   
           if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_on, sizeof(sock_on)) ||
               setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_on, sizeof(sock_on)) ||
               ::bind(sock, (struct sockaddr*)&me, sizeof(me)) ||
               listen(sock, 16)) {
                   closesocket(sock);
                   int no = pa_socks_errno();
                   throw Exception("httpd.bind", 0, "can not bind socket: %s (%d)", pa_socks_strerr(no), no);
           }
           return sock;
   }

Removed from v.1.83  
changed lines
  Added in v.1.97


E-mail: