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: