Annotation of parser3/src/targets/cgi/pa_sapi_info.h, revision 1.27

1.1       moko        1: #ifndef PA_SAPI_INFO_H
                      2: #define PA_SAPI_INFO_H
                      3: 
1.27    ! moko        4: #define IDENT_PA_SAPI_INFO_H "$Id: pa_sapi_info.h,v 1.26 2026/10/27 23:05:00 moko Exp $"
1.1       moko        5: 
                      6: #include "pa_sapi.h"
                      7: #include "pa_http.h"
                      8: 
                      9: /// IIS refuses to read bigger chunks
                     10: const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M
1.20      moko       11: const int HEADERS_SENT = 999; // can't send error message after headers have been sent
1.1       moko       12: 
                     13: class SAPI_Info : public PA_Allocated {
                     14: public:
                     15:        int http_response_code;
1.20      moko       16:        String::Body headers;
1.1       moko       17: 
1.7       moko       18:        SAPI_Info() : http_response_code(200) {}
1.1       moko       19: 
                     20:        virtual char* get_env(const char* name) {
                     21:                if(char *local=getenv(name))
                     22:                        return pa_strdup(local);
                     23:                else
                     24:                        return 0;
                     25:        }
                     26: 
1.10      moko       27:        virtual bool set_env(const char*, const char*) {
1.8       moko       28:                return false;
                     29:        }
                     30: 
1.1       moko       31:        virtual const char* const *get_env() {
                     32: #ifdef _MSC_VER
                     33:                extern char **_environ;
                     34:                return _environ;
                     35: #else
                     36:                extern char **environ;
                     37:                return environ;
                     38: #endif
                     39:        }
                     40: 
                     41:        virtual size_t read_post(char *, size_t) {
                     42:                return 0;
                     43:        }
                     44: 
1.20      moko       45:        virtual void add_header(const char* dont_store_key, const char* dont_store_value) {
                     46:                if(strcasecmp(dont_store_key, "location")==0)
                     47:                        http_response_code=302;
1.1       moko       48:                if(strcasecmp(dont_store_key, HTTP_STATUS)==0)
                     49:                        http_response_code=atoi(dont_store_value);
                     50:        }
                     51: 
1.20      moko       52:        virtual void send_headers() {}
1.1       moko       53: 
1.21      moko       54:        void clear_headers() {
                     55:                http_response_code=200;
                     56:                headers.clear();
                     57:        }
                     58: 
1.1       moko       59:        virtual size_t send_body(const void *buf, size_t size) {
                     60:                return stdout_write(buf, size);
                     61:        }
                     62: 
1.20      moko       63:        virtual void send_error(const char *exception_cstr, const char *status){
                     64:                http_response_code=atoi(status);
                     65:                send_body(exception_cstr, strlen(exception_cstr));
                     66:        }
1.19      moko       67: };
1.1       moko       68: 
                     69: class SAPI_Info_CGI : public SAPI_Info {
                     70: public:
                     71: 
                     72:        virtual size_t read_post(char *buf, size_t max_bytes) {
                     73:                size_t read_size=0;
                     74:                do {
                     75:                        ssize_t chunk_size=read(fileno(stdin), buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
                     76:                        if(chunk_size<=0)
                     77:                        break;
                     78:                        read_size+=chunk_size;
                     79:                } while(read_size<max_bytes);
                     80:                return read_size;
                     81:        }
                     82: 
1.20      moko       83:        virtual void add_header(const char* dont_store_key, const char* dont_store_value) {
                     84:                headers << capitalize(dont_store_key) << ": " << pa_strdup(dont_store_value) << "\r\n";
1.1       moko       85:        }
                     86: 
1.20      moko       87:        virtual void send_headers() {
1.27    ! moko       88:                if(!thread_request || !thread_request->console.was_used()){
1.20      moko       89:                        headers << "\r\n";
                     90:                        send_body(headers.cstr(), headers.length());
                     91:                        http_response_code=HEADERS_SENT;
                     92:                }
1.1       moko       93:        }
                     94: 
1.20      moko       95:        virtual void send_error(const char *exception_cstr, const char *status){
                     96:                if (http_response_code==HEADERS_SENT)
                     97:                        return;
                     98:                // memory allocation is not allowed
                     99:                char buf[MAX_STRING];
1.24      moko      100:                pa_snprintf(buf, MAX_STRING, HTTP_STATUS_CAPITALIZED ": %s\r\n"
1.20      moko      101:                                HTTP_CONTENT_TYPE_CAPITALIZED ": text/plain\r\n\r\n", status);
                    102:                send_body(buf, strlen(buf));
                    103:                send_body(exception_cstr, strlen(exception_cstr));
                    104:        }
1.1       moko      105: 
                    106: };
                    107: 
1.20      moko      108: static char* replace_char(char* str, char from, char to){
1.3       moko      109:     for(char *pos = strchr(str,from); pos; pos=strchr(pos,from)) {
                    110:         *pos = to;
                    111:     }
                    112:     return str;
                    113: }
                    114: 
1.1       moko      115: class SAPI_Info_HTTPD : public SAPI_Info {
                    116: public:
                    117: 
                    118:        HTTPD_Connection &connection;
1.3       moko      119:        HashStringString env;
1.1       moko      120: 
                    121:        SAPI_Info_HTTPD(HTTPD_Connection &aconnection) : connection(aconnection) {}
                    122: 
1.3       moko      123:        void populate_env() {
                    124:                String::Body host("localhost");
1.17      moko      125:                for(Array_iterator<HTTP_Headers::Header> i(connection.headers()); i; ){
1.2       moko      126:                        HTTP_Headers::Header header=i.next();
1.3       moko      127:                        String name("HTTP_");
                    128:                        name << replace_char(header.name.cstrm(), '-', '_');
                    129:                        String::Body value=header.value;
                    130: 
                    131:                        if(header.name == "HOST"){
                    132:                                size_t port=value.pos(':');
                    133:                                if(port != STRING_NOT_FOUND)
                    134:                                        value=value.mid(0, port);
                    135:                                host=value;
                    136:                        }
                    137:                        env.put(name, value);
1.1       moko      138:                }
1.3       moko      139: 
1.22      moko      140:                HASH_PUT_CSTR(env, "REQUEST_METHOD", String::Body(connection.method()));
                    141:                HASH_PUT_CSTR(env, "REQUEST_URI", String::Body(connection.uri()));
                    142:                HASH_PUT_CSTR(env, "QUERY_STRING", String::Body(connection.query()));
                    143: 
                    144:                HASH_PUT_CSTR(env, "SERVER_NAME", host);
                    145:                HASH_PUT_CSTR(env, "SERVER_PORT", String::Body(HTTPD_Server::port));
                    146:                HASH_PUT_CSTR(env, "REMOTE_ADDR", String::Body(connection.remote_addr));
1.3       moko      147: 
1.1       moko      148:        }
                    149: 
1.3       moko      150:        virtual char* get_env(const char* name) {
1.16      moko      151:                String::Body request_value = env.get(name);
                    152:                if(!request_value.is_empty())
                    153:                        return request_value.cstrm();
                    154:                if(char *server_value=getenv(name))
                    155:                        return pa_strdup(server_value);
                    156:                return NULL;
1.1       moko      157:        }
                    158: 
1.8       moko      159:        virtual bool set_env(const char* name, const char* value) {
                    160:                env.put(name, *new String(value));
                    161:                return true;
                    162:        }
                    163: 
1.1       moko      164:        virtual const char* const *get_env() {
1.3       moko      165:                const char** result=new(PointerGC) const char*[env.count()+1/*0*/];
1.1       moko      166:                const char** cur=result;
1.3       moko      167:                for(HashStringString::Iterator i(env); i; i.next()){
                    168:                        String pair;
                    169:                        pair << i.key() << "=" << i.value();
                    170:                        *cur++=pair.cstr();
1.1       moko      171:                }
                    172:                *cur=NULL;
                    173:                return result;
                    174:        }
                    175: 
1.4       moko      176:        virtual size_t read_post(char *buf, size_t max_bytes) {
                    177:                return connection.read_post(buf, max_bytes);
1.1       moko      178:        }
                    179: 
1.6       moko      180:        static const char *exception_http_status(const char *type) {
                    181:                struct Lookup {
                    182:                        const char *code;
                    183:                        const char *type;
                    184:                } static lookup[] = {
1.13      moko      185:                        {  "",  "httpd.write"},
1.6       moko      186:                        {"400", "httpd.request"},
1.9       moko      187:                        {"400", "http.response"},
1.6       moko      188:                        {"404", "file.missing"},
                    189:                        {"408", "httpd.timeout"},
1.13      moko      190:                        {"408", "httpd.read"},
1.6       moko      191:                        {"501", "httpd.method"},
                    192:                        { NULL, ""}
                    193:                };
                    194:                Lookup *cur = lookup;
                    195:                for(; cur->code; cur++)
                    196:                        if(!strcmp(type, cur->type))
                    197:                                return cur->code;
                    198:                return "500";
                    199:        }
                    200: 
                    201:        static const char *status_message(int code) {
1.1       moko      202:                struct Lookup {
                    203:                        int code;
                    204:                        const char *message;
                    205:                } static lookup[] = {
1.23      moko      206:                        {101, "Switching Protocols"},
1.1       moko      207:                        {200, "OK"},
1.11      moko      208:                        {204, "No Content"},
1.1       moko      209:                        {206, "Partial Content"},
                    210:                        {301, "Moved Permanently"},
                    211:                        {302, "Found"},
1.6       moko      212:                        {304, "Not Modified"},
1.1       moko      213:                        {400, "Bad Request"},
                    214:                        {401, "Unauthorized"},
                    215:                        {403, "Forbidden"},
                    216:                        {404, "Not Found"},
1.6       moko      217:                        {408, "Request Timeout"},
1.11      moko      218:                        {416, "Range Not Satisfiable"},
1.1       moko      219:                        {500, "Internal Server Error"},
                    220:                        {501, "Not Implemented"},
                    221:                        {502, "Bad Gateway"},
1.25      moko      222:                        {503, "Service Unavailable"},
1.1       moko      223:                        {504, "Gateway Timeout"},
                    224:                        {  0, "Undescribed"}
                    225:                };
                    226:                Lookup *cur = lookup;
                    227:                for(; cur->code; cur++)
                    228:                        if(code == cur->code)
                    229:                                return cur->message;
                    230:                return cur->message;
                    231:        }
                    232: 
1.20      moko      233:        virtual void add_header(const char* dont_store_key, const char* dont_store_value) {
                    234:                if(strcasecmp(dont_store_key, "location")==0)
                    235:                        http_response_code=302;
                    236:                if(strcasecmp(dont_store_key, HTTP_STATUS)==0)
                    237:                        http_response_code=atoi(dont_store_value);
                    238:                else
                    239:                        headers << capitalize(dont_store_key) << ": " << pa_strdup(dont_store_value) << "\r\n";
                    240:        }
                    241: 
                    242:        virtual void send_headers() {
1.23      moko      243:                // the handshake of WebSocket requires HTTP/1.1
                    244:                String result(http_response_code==101 ? "HTTP/1.1 " : "HTTP/1.0 ");
1.20      moko      245:                result << pa_uitoa(http_response_code) << " " << status_message(http_response_code) << "\r\n" << headers << "\r\n";
1.1       moko      246:                send_body(result.cstr(), result.length());
1.20      moko      247:                http_response_code=HEADERS_SENT;
1.1       moko      248:        }
                    249: 
                    250:        virtual size_t send_body(const void *buf, size_t size) {
1.5       moko      251:                return connection.send_body(buf, size);
1.1       moko      252:        }
                    253: 
1.20      moko      254:        virtual void send_error(const char *exception_cstr, const char *status){
                    255:                if (http_response_code==HEADERS_SENT)
                    256:                        return;
                    257:                // memory allocation is not allowed
                    258:                char buf[MAX_STRING];
1.24      moko      259:                pa_snprintf(buf, MAX_STRING, "HTTP/1.0 %s %s\r\n"
1.20      moko      260:                                HTTP_CONTENT_TYPE_CAPITALIZED ": text/plain\r\n\r\n", status, status_message(atoi(status)));
                    261:                send_body(buf, strlen(buf));
                    262:                send_body(exception_cstr, strlen(exception_cstr));
                    263:        }
                    264: 
1.1       moko      265: };
                    266: 
                    267: #endif

E-mail: