int uwsgi_add_expires_uri(struct wsgi_request *wsgi_req, struct stat *st) { struct uwsgi_dyn_dict *udd = uwsgi.static_expires_uri; time_t now = wsgi_req->start_of_request / 1000000; // 30+1 char expires[31]; while (udd) { if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = set_http_date(now + delta, expires); if (size > 0) { if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1; } return 0; } udd = udd->next; } udd = uwsgi.static_expires_uri_mtime; while (udd) { if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = set_http_date(st->st_mtime + delta, expires); if (size > 0) { if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1; } return 0; } udd = udd->next; } return 0; }
int uwsgi_websocket_handshake(struct wsgi_request *wsgi_req, char *key, uint16_t key_len, char *origin, uint16_t origin_len) { #ifdef UWSGI_SSL char sha1[20]; if (uwsgi_response_prepare_headers(wsgi_req, "101 Web Socket Protocol Handshake", 33)) return -1; if (uwsgi_response_add_header(wsgi_req, "Upgrade", 7, "WebSocket", 9)) return -1; if (uwsgi_response_add_header(wsgi_req, "Connection", 10, "Upgrade", 7)) return -1; if (origin_len > 0) { if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Origin", 20, origin, origin_len)) return -1; } else { if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Origin", 20, "*", 1)) return -1; } // generate websockets sha1 and encode it to base64 if (!uwsgi_sha1_2n(key, key_len, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36, sha1)) return -1; size_t b64_len = 0; char *b64 = uwsgi_base64_encode(sha1, 20, &b64_len); if (!b64) return -1; if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Accept", 20, b64, b64_len)) { free(b64); return -1; } free(b64); wsgi_req->websocket_last_pong = uwsgi_now(); return uwsgi_response_write_headers_do(wsgi_req); #else uwsgi_log("you need to build uWSGI with SSL support to use the websocket handshake api function !!!\n"); return -1; #endif }
int uwsgi_add_expires_type(struct wsgi_request *wsgi_req, char *mime_type, int mime_type_len, struct stat *st) { struct uwsgi_dyn_dict *udd = uwsgi.static_expires_type; time_t now = wsgi_req->start_of_request / 1000000; // 30+1 char expires[31]; while (udd) { if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = set_http_date(now + delta, expires); if (size > 0) { if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1; } return 0; } udd = udd->next; } udd = uwsgi.static_expires_type_mtime; while (udd) { if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = set_http_date(st->st_mtime + delta, expires); if (size > 0) { if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1; } return 0; } udd = udd->next; } return 0; }
Response& Response::flush() { if ( !status_.empty() ) { uwsgi_response_prepare_headers( r_, (char*)status_.c_str(), status_.size() ); status_ = ""; } for ( size_t i = 0; i < headers_.size(); i++) { const std::string& key = headers_[i].first; const std::string& val = headers_[i].second; uwsgi_response_add_header( r_, (char*)key.c_str(), key.size(), (char*)val.c_str(), val.size() ); headers_.clear(); } if ( !cookies_.empty()) { for ( size_t i = 0; i < cookies_.size(); i++ ) { std::string key = "Set-Cookie"; std::string val = cookies_[i].str(); uwsgi_response_add_header( r_, (char*)key.c_str(), key.size(), (char*)val.c_str(), val.size() ); } cookies_.clear(); } if ( !body_.empty() ) { uwsgi_response_write_body_do( r_, (char*)body_.c_str(), body_.size() ); body_ = ""; } return *this; }
int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { if (wsgi_req->headers_sent || !wsgi_req->headers || wsgi_req->response_size || wsgi_req->write_errors) { return UWSGI_OK; } struct uwsgi_string_list *ah = uwsgi.additional_headers; while(ah) { if (uwsgi_response_add_header(wsgi_req, NULL, 0, ah->value, ah->len)) return -1; ah = ah->next; } ah = wsgi_req->additional_headers; while(ah) { if (uwsgi_response_add_header(wsgi_req, NULL, 0, ah->value, ah->len)) return -1; ah = ah->next; } if (wsgi_req->socket->proto_fix_headers(wsgi_req)) { wsgi_req->write_errors++ ; return -1;} for(;;) { int ret = wsgi_req->socket->proto_write_headers(wsgi_req, wsgi_req->headers->buf, wsgi_req->headers->pos); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_error("uwsgi_response_write_headers_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_write_headers_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } wsgi_req->headers_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; wsgi_req->headers_sent = 1; return UWSGI_OK; }
static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers) { sapi_header_struct *h; zend_llist_position pos; if (SG(request_info).no_headers == 1) { return SAPI_HEADER_SENT_SUCCESSFULLY; } struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); if (!SG(sapi_headers).http_status_line) { char status[4]; int hrc = SG(sapi_headers).http_response_code; if (!hrc) hrc = 200; uwsgi_num2str2n(hrc, status, 4); uwsgi_response_prepare_headers(wsgi_req, status, 3); } else { char *sl = SG(sapi_headers).http_status_line; uwsgi_response_prepare_headers(wsgi_req, sl, strlen(sl)); } h = zend_llist_get_first_ex(&sapi_headers->headers, &pos); while (h) { uwsgi_response_add_header(wsgi_req, NULL, 0, h->header, h->header_len); h = zend_llist_get_next_ex(&sapi_headers->headers, &pos); } return SAPI_HEADER_SENT_SUCCESSFULLY; }
static int uwsgi_routing_func_basicauth(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { // skip if already authenticated if (wsgi_req->remote_user_len > 0) { return UWSGI_ROUTE_NEXT; } if (wsgi_req->authorization_len > 7 && ur->data2_len > 0) { if (strncmp(wsgi_req->authorization, "Basic ", 6)) goto forbidden; size_t auth_len = 0; char *auth = uwsgi_base64_decode(wsgi_req->authorization+6, wsgi_req->authorization_len-6, &auth_len); if (auth) { if (!ur->custom) { // check htpasswd-like file uint16_t ulen = htpasswd_check(ur->data2, auth); if (ulen > 0) { wsgi_req->remote_user = uwsgi_req_append(wsgi_req, "REMOTE_USER", 11, auth, ulen); if (!wsgi_req->remote_user) { free(auth); goto forbidden; } wsgi_req->remote_user_len = ulen; } else if (ur->data3_len == 0) { free(auth); goto forbidden; } } else { if (!uwsgi_strncmp(auth, auth_len, ur->data2, ur->data2_len)) { wsgi_req->remote_user = uwsgi_req_append(wsgi_req, "REMOTE_USER", 11, auth, ur->custom); if (!wsgi_req->remote_user) { free(auth); goto forbidden; } wsgi_req->remote_user_len = ur->custom; } else if (ur->data3_len == 0) { free(auth); goto forbidden; } } free(auth); return UWSGI_ROUTE_NEXT; } } forbidden: if (uwsgi_response_prepare_headers(wsgi_req, "401 Authorization Required", 26)) goto end; char *realm = uwsgi_concat3n("Basic realm=\"", 13, ur->data, ur->data_len, "\"", 1); // no need to check for errors uwsgi_response_add_header(wsgi_req, "WWW-Authenticate", 16, realm, 13 + ur->data_len + 1); free(realm); uwsgi_response_write_body_do(wsgi_req, "Unauthorized", 12); end: return UWSGI_ROUTE_BREAK; }
int uwsgi_response_add_content_length(struct wsgi_request *wsgi_req, uint64_t cl) { char buf[sizeof(UMAX64_STR)+1]; int ret = snprintf(buf, sizeof(UMAX64_STR)+1, "%llu", (unsigned long long) cl); if (ret <= 0 || ret > (int) (sizeof(UMAX64_STR)+1)) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Content-Length", 14, buf, ret); }
int uwsgi_response_add_last_modified(struct wsgi_request *wsgi_req, uint64_t t) { // 30+1 char lm[31]; int len = uwsgi_http_date((time_t) t, lm); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, lm, len); }
int uwsgi_response_add_date(struct wsgi_request *wsgi_req, char *hkey, uint16_t hlen, uint64_t t) { // 30+1 char d[31]; int len = uwsgi_http_date((time_t) t, d); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, hkey, hlen, d, len); }
int uwsgi_response_add_expires(struct wsgi_request *wsgi_req, uint64_t t) { // 30+1 char expires[31]; int len = uwsgi_http_date((time_t) t, expires); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, len); }
/* OPTIONS: if it is a valid webdav resource add Dav: to the response header */ static int uwsgi_wevdav_manage_options(struct wsgi_request *wsgi_req) { uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6); if (udav.add_option) { struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); if (uwsgi_buffer_append(ub, "1, 2, 3", 7)) goto end; struct uwsgi_string_list *usl = udav.add_option; while(usl) { if (uwsgi_buffer_append(ub, ", ", 2)) goto end; if (uwsgi_buffer_append(ub, usl->value, usl->len)) goto end; usl = usl->next; } uwsgi_response_add_header(wsgi_req, "Dav", 3, ub->buf, ub->pos); end: uwsgi_buffer_destroy(ub); } else { uwsgi_response_add_header(wsgi_req, "Dav", 3, "1, 2, 3", 7); } return UWSGI_OK; }
int uwsgi_response_add_content_range(struct wsgi_request *wsgi_req, uint64_t start, uint64_t end, uint64_t cl) { char buf[6+(sizeof(UMAX64_STR)*3)+4]; if (end == 0) { end = cl-1; } int ret = snprintf(buf, 6+(sizeof(UMAX64_STR)*3)+4, "bytes %llu-%llu/%llu", (unsigned long long) start, (unsigned long long) end, (unsigned long long) cl); if (ret <= 0 || ret > (int) (6+(sizeof(UMAX64_STR)*3)+4)) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Content-Range", 13, buf, ret); }
int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, size_t real_filename_len, struct stat *st) { size_t mime_type_size = 0; char http_last_modified[49]; if (uwsgi.threads > 1) pthread_mutex_lock(&uwsgi.lock_static); char *mime_type = uwsgi_get_mime_type(real_filename, real_filename_len, &mime_type_size); if (uwsgi.threads > 1) pthread_mutex_unlock(&uwsgi.lock_static); if (wsgi_req->if_modified_since_len) { time_t ims = parse_http_date(wsgi_req->if_modified_since, wsgi_req->if_modified_since_len); if (st->st_mtime <= ims) { uwsgi_response_prepare_headers(wsgi_req, "304 Not Modified", 16); return uwsgi_response_write_headers_do(wsgi_req); } } #ifdef UWSGI_DEBUG uwsgi_log("[uwsgi-fileserve] file %s found\n", real_filename); #endif size_t fsize = st->st_size; if (wsgi_req->range_to) { fsize = wsgi_req->range_to - wsgi_req->range_from; if (fsize > (size_t)st->st_size) { fsize = st->st_size; } } else { // reset in case of inconsistent size if (wsgi_req->range_from > fsize) { wsgi_req->range_from = 0; fsize = 0 ; } else { fsize -= wsgi_req->range_from; } } // HTTP status if (fsize > 0 && (wsgi_req->range_from || wsgi_req->range_to)) { if (uwsgi_response_prepare_headers(wsgi_req, "206 Partial Content", 19)) return -1; } else { if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; } #ifdef UWSGI_PCRE uwsgi_add_expires(wsgi_req, real_filename, real_filename_len, st); uwsgi_add_expires_path_info(wsgi_req, st); uwsgi_add_expires_uri(wsgi_req, st); #endif // Content-Type (if available) if (mime_type_size > 0 && mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_size)) return -1; // check for content-type related headers uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, st); } // increase static requests counter uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].static_requests++; // nginx if (uwsgi.file_serve_mode == 1) { if (uwsgi_response_add_header(wsgi_req, "X-Accel-Redirect", 16, real_filename, real_filename_len)) return -1; // this is the final header (\r\n added) int size = set_http_date(st->st_mtime, http_last_modified); if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1; } // apache else if (uwsgi.file_serve_mode == 2) { if (uwsgi_response_add_header(wsgi_req, "X-Sendfile", 10, real_filename, real_filename_len)) return -1; // this is the final header (\r\n added) int size = set_http_date(st->st_mtime, http_last_modified); if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1; } // raw else { // here we need to choose if we want the gzip variant; if (uwsgi_static_want_gzip(wsgi_req, real_filename, real_filename_len, st)) { if (uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "gzip", 4)) return -1; } // set Content-Length (to fsize NOT st->st_size) if (uwsgi_response_add_content_length(wsgi_req, fsize)) return -1; if (fsize > 0 && (wsgi_req->range_from || wsgi_req->range_to)) { // here use teh original size !!! if (uwsgi_response_add_content_range(wsgi_req, wsgi_req->range_from, wsgi_req->range_to, st->st_size)) return -1; } int size = set_http_date(st->st_mtime, http_last_modified); if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1; // if it is a HEAD request just skip transfer if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { wsgi_req->status = 200; return 0; } // Ok, the file must be transferred from uWSGI // offloading will be automatically managed int fd = open(real_filename, O_RDONLY); if (fd < 0) return -1; // fd will be closed in the following function uwsgi_response_sendfile_do(wsgi_req, fd, wsgi_req->range_from, fsize); } wsgi_req->status = 200; return 0; }
static int uwsgi_wevdav_manage_get(struct wsgi_request *wsgi_req, int send_body) { char filename[PATH_MAX]; size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename); if (!filename_len) { uwsgi_404(wsgi_req); return UWSGI_OK; } if (uwsgi_is_dir(filename)) { uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6); if (send_body) { uwsgi_webdav_dirlist(wsgi_req, filename); } return UWSGI_OK; } int fd = open(filename, O_RDONLY); if (fd < 0) { uwsgi_403(wsgi_req); return UWSGI_OK; } struct stat st; if (fstat(fd, &st)) { close(fd); uwsgi_403(wsgi_req); return UWSGI_OK; } if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; // add content_length if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) goto end; // add last-modified if (uwsgi_response_add_last_modified(wsgi_req, st.st_mtime)) goto end; // add mime_type size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filename, filename_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } // add ETag (based on file mtime, not rock-solid, but good enough) char *etag = uwsgi_num2str(st.st_mtime); if (uwsgi_response_add_header(wsgi_req, "ETag", 4, etag, strlen(etag))) { free(etag); goto end; } free(etag); // start sending the file (note: we do not use sendfile() api, for being able to use caching and transformations) if (!send_body) goto end; // use a pretty big buffer (for performance reasons) char buf[32768]; size_t remains = st.st_size; while (remains > 0) { ssize_t rlen = read(fd, buf, UMIN(32768, remains)); if (rlen <= 0) { uwsgi_error("uwsgi_wevdav_manage_get/read()"); break; } remains -= rlen; if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) { break; } } end: close(fd); return UWSGI_OK; }
int uwsgi_jvm_iterator_to_response_headers(struct wsgi_request *wsgi_req, jobject headers) { int error = 0; while(uwsgi_jvm_iterator_hasNext(headers)) { jobject hh = NULL, h_key = NULL, h_value = NULL; hh = uwsgi_jvm_iterator_next(headers); if (!hh) { error = 1 ; goto clear;} h_key = uwsgi_jvm_getKey(hh); if (!h_key) { error = 1 ; goto clear;} h_value = uwsgi_jvm_getValue(hh); if (!h_value) { error = 1 ; goto clear;} if (!uwsgi_jvm_object_is_instance(h_key, ujvm.str_class)) { uwsgi_log("headers key must be java/lang/String !!!\n"); error = 1 ; goto clear; } // check for string if (uwsgi_jvm_object_is_instance(h_value, ujvm.str_class)) { char *c_h_key = uwsgi_jvm_str2c(h_key); uint16_t c_h_keylen = uwsgi_jvm_strlen(h_key); char *c_h_value = uwsgi_jvm_str2c(h_value); uint16_t c_h_vallen = uwsgi_jvm_strlen(h_value); int ret = uwsgi_response_add_header(wsgi_req, c_h_key, c_h_keylen, c_h_value, c_h_vallen); uwsgi_jvm_release_chars(h_key, c_h_key); uwsgi_jvm_release_chars(h_value, c_h_value); if (ret) error = 1; goto clear; } // check for string array if (uwsgi_jvm_object_is_instance(h_value, ujvm.str_array_class)) { size_t items = uwsgi_jvm_array_len(h_value); size_t i; for(i=0;i<items;i++) { jobject hh_value = uwsgi_jvm_array_get(h_value, i); if (!uwsgi_jvm_object_is_instance(hh_value, ujvm.str_class)) { uwsgi_log("headers value must be java/lang/String !!!\n"); uwsgi_jvm_local_unref(hh_value); error = 1 ; goto clear; } char *c_h_key = uwsgi_jvm_str2c(h_key); uint16_t c_h_keylen = uwsgi_jvm_strlen(h_key); char *c_h_value = uwsgi_jvm_str2c(hh_value); uint16_t c_h_vallen = uwsgi_jvm_strlen(hh_value); int ret = uwsgi_response_add_header(wsgi_req, c_h_key, c_h_keylen, c_h_value, c_h_vallen); uwsgi_jvm_release_chars(h_key, c_h_key); uwsgi_jvm_release_chars(hh_value, c_h_value); uwsgi_jvm_local_unref(hh_value); if (ret) { error = 1 ; goto clear;} } goto clear; } // check for iterable jobject values = uwsgi_jvm_auto_iterator(h_value); if (values) { while(uwsgi_jvm_iterator_hasNext(values)) { jobject hh_value = uwsgi_jvm_iterator_next(values); if (!uwsgi_jvm_object_is_instance(hh_value, ujvm.str_class)) { uwsgi_log("headers value must be java/lang/String !!!\n"); uwsgi_jvm_local_unref(hh_value); uwsgi_jvm_local_unref(values); error = 1 ; goto clear; } char *c_h_key = uwsgi_jvm_str2c(h_key); uint16_t c_h_keylen = uwsgi_jvm_strlen(h_key); char *c_h_value = uwsgi_jvm_str2c(hh_value); uint16_t c_h_vallen = uwsgi_jvm_strlen(hh_value); int ret = uwsgi_response_add_header(wsgi_req, c_h_key, c_h_keylen, c_h_value, c_h_vallen); uwsgi_jvm_release_chars(h_key, c_h_key); uwsgi_jvm_release_chars(hh_value, c_h_value); uwsgi_jvm_local_unref(hh_value); if (ret) { uwsgi_jvm_local_unref(values); error = 1 ; goto clear;} } uwsgi_jvm_local_unref(values); goto clear; } uwsgi_log("unsupported header value !!! (must be java/lang/String or [java/lang/String)\n"); error = 1; clear: if (h_value) uwsgi_jvm_local_unref(h_value); if (h_key) uwsgi_jvm_local_unref(h_key); if (hh) uwsgi_jvm_local_unref(hh); if (error) return -1;; } return 0; }
static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, int fd, char *buf, size_t blen) { size_t i; size_t header_size = 0; int status_sent = 0; size_t remains = blen; char *ptr = buf; size_t len = 0; while(remains > 0) { ssize_t rlen = uwsgi_read_true_nb(fd, ptr, remains, uc.timeout); if (rlen < 0) { if (!errno) return 1; return -1; } // timed out if (rlen == 0) return -1; remains -= rlen; len += rlen; ptr += rlen; // Search for Status/Location headers if (!status_sent) { status_sent = uwsgi_cgi_check_status(wsgi_req, buf, len); if (status_sent < 0) return -1; // need more data ? if (status_sent == 0) continue; } // send headers char *key = buf; char *value = NULL; for(i=0;i<len;i++) { // end of a line if (buf[i] == '\n') { // end of headers if (key == NULL) { i++; goto send_body; } // invalid header else if (value == NULL) { return -1; } header_size = (buf+i) - key; // security check if (buf+i > buf) { if ((buf[i-1]) == '\r') { header_size--; } } #ifdef UWSGI_DEBUG uwsgi_log("found CGI header: %.*s\n", header_size, key); #endif // Ignore "Status: NNN" header if (header_size >= 11) { if (!strncasecmp("Status: ", key, 8)) { key = NULL; value = NULL; continue; } } uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size); key = NULL; value = NULL; } else if (buf[i] == ':') { value = buf+i; } else if (buf[i] != '\r') { if (key == NULL) { key = buf + i; } } } } return -1; send_body: if (len-i > 0) { uwsgi_response_write_body_do(wsgi_req, buf+i, len-i); } return 0; }
int uwsgi_response_subhandler_pump(struct wsgi_request *wsgi_req) { PyObject *pychunk; int i; char sc[4]; // ok its a yield if (!wsgi_req->async_placeholder) { if (PyDict_Check((PyObject *)wsgi_req->async_result)) { PyObject *status = PyDict_GetItemString((PyObject *)wsgi_req->async_result, "status"); if (!status) { uwsgi_log("invalid Pump response (status code).\n"); goto clear; } PyObject *headers = PyDict_GetItemString((PyObject *)wsgi_req->async_result, "headers"); if (!headers) { uwsgi_log("invalid Pump response (headers).\n"); goto clear; } wsgi_req->async_placeholder = PyDict_GetItemString((PyObject *)wsgi_req->async_result, "body"); if (!wsgi_req->async_placeholder) { uwsgi_log("invalid Pump response (body).\n"); goto clear; } // get the status code if (!PyInt_Check(status)) { uwsgi_log("invalid Pump response (status code).\n"); goto clear; } if (uwsgi_num2str2n(PyInt_AsLong(status), sc, 4) != 3) { uwsgi_log("invalid Pump response (status code).\n"); goto clear; } if (uwsgi_response_prepare_headers(wsgi_req, sc, 3)) { uwsgi_log("unable to prepare response headers\n"); } PyObject *hhkey, *hhvalue; #ifdef UWSGI_PYTHON_OLD int hhpos = 0; #else Py_ssize_t hhpos = 0; #endif while (PyDict_Next(headers, &hhpos, &hhkey, &hhvalue)) { if (!PyString_Check(hhkey)) continue; char *k = PyString_AsString(hhkey); size_t kl = PyString_Size(hhkey); k[0] = toupper((int) k[0]); if (PyList_Check(hhvalue)) { for(i=0;i<PyList_Size(hhvalue);i++) { PyObject *item = PyList_GetItem(hhvalue, i); if (PyString_Check(item)) { if (uwsgi_response_add_header(wsgi_req, k, kl, PyString_AsString(item), PyString_Size(item))) goto clear; } } } else if (PyString_Check(hhvalue)) { if (uwsgi_response_add_header(wsgi_req, k, kl, PyString_AsString(hhvalue), PyString_Size(hhvalue))) goto clear; } } Py_INCREF((PyObject *)wsgi_req->async_placeholder); if (PyString_Check((PyObject *)wsgi_req->async_placeholder)) { UWSGI_RELEASE_GIL uwsgi_response_write_body_do(wsgi_req, PyString_AsString(wsgi_req->async_placeholder), PyString_Size(wsgi_req->async_placeholder)); UWSGI_GET_GIL uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); } goto clear; } #ifdef PYTHREE else if ((wsgi_req->sendfile_fd = PyObject_AsFileDescriptor((PyObject *)wsgi_req->async_placeholder)) > -1) {
static int uwsgi_lua_request(struct wsgi_request *wsgi_req) { int i; const char *http, *http2; size_t slen, slen2; char *ptrbuf; lua_State *L = ulua.L[wsgi_req->async_id]; if (wsgi_req->async_status == UWSGI_AGAIN) { if ((i = lua_pcall(L, 0, 1, 0)) == 0) { if (lua_type(L, -1) == LUA_TSTRING) { http = lua_tolstring(L, -1, &slen); uwsgi_response_write_body_do(wsgi_req, (char *)http, slen); } lua_pop(L, 1); lua_pushvalue(L, -1); return UWSGI_AGAIN; } goto clear; } /* Standard WSAPI request */ if (!wsgi_req->uh->pktsize) { uwsgi_log( "Empty lua request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } // put function in the stack //lua_getfield(L, LUA_GLOBALSINDEX, "run"); lua_pushvalue(L, -1); // put cgi vars in the stack lua_newtable(L); lua_pushstring(L, ""); lua_setfield(L, -2, "CONTENT_TYPE"); for(i=0;i<wsgi_req->var_cnt;i++) { lua_pushlstring(L, (char *)wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len); // transform it in a valid c string TODO this is ugly ptrbuf = wsgi_req->hvec[i].iov_base+wsgi_req->hvec[i].iov_len; *ptrbuf = 0; lua_setfield(L, -2, (char *)wsgi_req->hvec[i].iov_base); i++; } // put "input" table lua_newtable(L); lua_pushcfunction(L, uwsgi_lua_input); lua_setfield(L, -2, "read"); lua_setfield(L, -2, "input"); #ifdef UWSGI_DEBUG uwsgi_log("stack pos %d\n", lua_gettop(L)); #endif // call function i = lua_pcall(L, 1, 3, 0); if (i != 0) { uwsgi_log("%s\n", lua_tostring(L, -1)); lua_pop(L, 1); lua_pushvalue(L, -1); goto clear; } //uwsgi_log("%d %s %s %s\n",i,lua_typename(L, lua_type(L, -3)), lua_typename(L, lua_type(L, -2)) , lua_typename(L, lua_type(L, -1))); // send status if (lua_type(L, -3) == LUA_TSTRING || lua_type(L, -3) == LUA_TNUMBER) { http = lua_tolstring(L, -3, &slen); uwsgi_response_prepare_headers(wsgi_req, (char *) http, slen); } else { uwsgi_log("[uwsgi-lua] invalid response status !!!\n"); // let's continue } // send headers lua_pushnil(L); while(lua_next(L, -3) != 0) { http = lua_tolstring(L, -2, &slen); http2 = lua_tolstring(L, -1, &slen2); uwsgi_response_add_header(wsgi_req, (char *) http, slen, (char *) http2, slen2); lua_pop(L, 1); } // send body with coroutine lua_pushvalue(L, -1); while ( (i = lua_pcall(L, 0, 1, 0)) == 0) { if (lua_type(L, -1) == LUA_TSTRING) { http = lua_tolstring(L, -1, &slen); uwsgi_response_write_body_do(wsgi_req, (char *)http, slen); } lua_pop(L, 1); lua_pushvalue(L, -1); if (uwsgi.async > 1) { return UWSGI_AGAIN; } } clear: lua_pop(L, 4); // set frequency lua_gc(L, LUA_GCCOLLECT, 0); return UWSGI_OK; }
static int uwsgi_rados_request(struct wsgi_request *wsgi_req) { char filename[PATH_MAX+1]; if (!wsgi_req->len) { uwsgi_log( "Empty request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } // blocks empty paths if (wsgi_req->path_info_len == 0 || wsgi_req->path_info_len > PATH_MAX) { uwsgi_403(wsgi_req); return UWSGI_OK; } wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, rados_plugin.modifier1); if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { if (uwsgi_apps[uwsgi.default_app].modifier1 == rados_plugin.modifier1) { wsgi_req->app_id = uwsgi.default_app; } } if (wsgi_req->app_id == -1) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; if (wsgi_req->path_info_len > ua->mountpoint_len && memcmp(wsgi_req->path_info, ua->mountpoint, ua->mountpoint_len) == 0) { memcpy(filename, wsgi_req->path_info+ua->mountpoint_len, wsgi_req->path_info_len-ua->mountpoint_len); filename[wsgi_req->path_info_len-ua->mountpoint_len] = 0; } else { memcpy(filename, wsgi_req->path_info, wsgi_req->path_info_len); filename[wsgi_req->path_info_len] = 0; } // in multithread mode the memory is different (as we need a ctx for each thread) !!! rados_ioctx_t ctx; if (uwsgi.threads > 1) { rados_ioctx_t *ctxes = (rados_ioctx_t *) ua->responder0; ctx = ctxes[wsgi_req->async_id]; } else { ctx = (rados_ioctx_t) ua->responder0; } struct uwsgi_rados_mountpoint *urmp = (struct uwsgi_rados_mountpoint *) ua->responder1; uint64_t stat_size = 0; time_t stat_mtime = 0; struct uwsgi_rados_io *urio = &urados.urio[wsgi_req->async_id]; if (uwsgi.async > 0) { // no need to lock here (the rid protect us) if (pipe(urio->fds)) { uwsgi_error("uwsgi_rados_read_async()/pipe()"); uwsgi_500(wsgi_req); return UWSGI_OK; } } int ret = -1; int timeout = urmp->timeout ? urmp->timeout : urados.timeout; if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "OPTIONS", 7)) { if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; if (uwsgi_response_add_header(wsgi_req, "Dav", 3, "1", 1)) goto end; struct uwsgi_buffer *ub_allow = uwsgi_buffer_new(64); if (uwsgi_buffer_append(ub_allow, "OPTIONS, GET, HEAD", 18)) { uwsgi_buffer_destroy(ub_allow); goto end; } if (urmp->allow_put) { if (uwsgi_buffer_append(ub_allow, ", PUT", 5)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_delete) { if (uwsgi_buffer_append(ub_allow, ", DELETE", 8)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_mkcol) { if (uwsgi_buffer_append(ub_allow, ", MKCOL", 7)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_propfind) { if (uwsgi_buffer_append(ub_allow, ", PROPFIND", 10)) { uwsgi_buffer_destroy(ub_allow); goto end; } } uwsgi_response_add_header(wsgi_req, "Allow", 5, ub_allow->buf, ub_allow->pos); uwsgi_buffer_destroy(ub_allow); goto end; } // empty paths are mapped to propfind if (wsgi_req->path_info_len == 1 && wsgi_req->path_info[0] == '/') { if (urmp->allow_propfind && !uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) { uwsgi_rados_propfind(wsgi_req, ctx, NULL, 0, 0, timeout); goto end; } uwsgi_405(wsgi_req); goto end; } // MKCOL does not require stat if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "MKCOL", 5)) { if (!urmp->allow_mkcol) { uwsgi_405(wsgi_req); goto end; } ret = rados_pool_create(urmp->cluster, filename); if (ret < 0) { if (ret == -EEXIST) { uwsgi_405(wsgi_req); } else { uwsgi_500(wsgi_req); } goto end; } uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11); goto end; } if (uwsgi.async > 0) { ret = uwsgi_rados_async_stat(urio, ctx, filename, &stat_size, &stat_mtime, timeout); } else { ret = rados_stat(ctx, filename, &stat_size, &stat_mtime); } // PUT AND MKCOL can be used for non-existent objects if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PUT", 3)) { if (!urmp->allow_put) { uwsgi_405(wsgi_req); goto end; } if (ret == 0) { if (uwsgi_rados_delete(wsgi_req, ctx, filename, timeout)) { uwsgi_500(wsgi_req); goto end; } } if (uwsgi_rados_put(wsgi_req, ctx, filename, urmp->put_buffer_size, timeout)) { uwsgi_500(wsgi_req); goto end; } uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11); goto end; } else if (ret < 0) { if (ret == -ENOENT) uwsgi_404(wsgi_req); else uwsgi_403(wsgi_req); goto end; } if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "DELETE", 6)) { if (!urmp->allow_delete) { uwsgi_405(wsgi_req); goto end; } if (uwsgi_rados_delete(wsgi_req, ctx, filename, timeout)) { uwsgi_403(wsgi_req); goto end; } uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6); goto end; } if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) { if (!urmp->allow_propfind) { uwsgi_405(wsgi_req); goto end; } uwsgi_rados_propfind(wsgi_req, ctx, filename, stat_size, stat_mtime, timeout); goto end; } if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4) && uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3)) { uwsgi_405(wsgi_req); goto end; } uint64_t offset = 0; uint64_t remains = stat_size; uwsgi_request_fix_range_for_size(wsgi_req, remains); switch (wsgi_req->range_parsed) { case UWSGI_RANGE_INVALID: if (uwsgi_response_prepare_headers(wsgi_req, "416 Requested Range Not Satisfiable", 35)) goto end; if (uwsgi_response_add_content_range(wsgi_req, -1, -1, stat_size)) goto end; return 0; case UWSGI_RANGE_VALID: offset = wsgi_req->range_from; remains = wsgi_req->range_to - wsgi_req->range_from + 1; if (uwsgi_response_prepare_headers(wsgi_req, "206 Partial Content", 19)) goto end; break; default: /* UWSGI_RANGE_NOT_PARSED */ if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; } size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(wsgi_req->path_info, wsgi_req->path_info_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } if (uwsgi_response_add_last_modified(wsgi_req, (uint64_t) stat_mtime)) goto end; // set Content-Length to actual result size if (uwsgi_response_add_content_length(wsgi_req, remains)) goto end; if (wsgi_req->range_parsed == UWSGI_RANGE_VALID) { // here use the original size !!! if (uwsgi_response_add_content_range(wsgi_req, wsgi_req->range_from, wsgi_req->range_to, stat_size)) goto end; } // skip body on HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { if (uwsgi.async > 0) { if (uwsgi_rados_read_async(wsgi_req, ctx, filename, offset, remains, urmp->buffer_size, timeout)) goto end; } else { if (uwsgi_rados_read_sync(wsgi_req, ctx, filename, offset, remains, urmp->buffer_size)) goto end; } } end: if (uwsgi.async > 0) { close(urio->fds[0]); close(urio->fds[1]); } return UWSGI_OK; }
PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) { PyObject *headers, *head; PyObject *h_key, *h_value; PyObject *exc_info = NULL; size_t i; struct wsgi_request *wsgi_req = py_current_wsgi_req(); // avoid double sending of headers if (wsgi_req->headers_sent) { return PyErr_Format(PyExc_IOError, "headers already sent"); } // this must be done before headers management if (PyTuple_Size(args) > 2) { exc_info = PyTuple_GetItem(args, 2); if (exc_info && exc_info != Py_None) { PyObject *exc_type = PyTuple_GetItem(exc_info, 0); PyObject *exc_val = PyTuple_GetItem(exc_info, 1); PyObject *exc_tb = PyTuple_GetItem(exc_info, 2); if (!exc_type || !exc_val || !exc_tb) { return NULL; } Py_INCREF(exc_type); Py_INCREF(exc_val); Py_INCREF(exc_tb); // in this way, error will be reported to the log PyErr_Restore(exc_type, exc_val, exc_tb); // the error is reported, let's continue... // return NULL } } head = PyTuple_GetItem(args, 0); if (!head) { return PyErr_Format(PyExc_TypeError, "start_response() takes at least 2 arguments"); } #ifdef PYTHREE // check for web3 if ((self != Py_None && !PyUnicode_Check(head)) || (self == Py_None && !PyBytes_Check(head))) { #else if (!PyString_Check(head)) { #endif return PyErr_Format(PyExc_TypeError, "http status must be a string"); } char *status_line = NULL; size_t status_line_len = 0; #ifdef PYTHREE PyObject *zero = NULL; PyObject *zero2 = NULL; if (self != Py_None) { zero = PyUnicode_AsASCIIString(head); if (!zero) { return PyErr_Format(PyExc_TypeError, "http status string must be encodable in latin1"); } status_line = PyBytes_AsString(zero); status_line_len = PyBytes_Size(zero); } else { status_line = PyBytes_AsString(head); status_line_len = PyBytes_Size(head); } #else status_line = PyString_AsString(head); status_line_len = PyString_Size(head); #endif if (uwsgi_response_prepare_headers(wsgi_req, status_line, status_line_len)) { #ifdef PYTHREE Py_DECREF(zero); #endif goto end; } #ifdef PYTHREE Py_DECREF(zero); #endif headers = PyTuple_GetItem(args, 1); if (!headers) { return PyErr_Format(PyExc_TypeError, "start_response() takes at least 2 arguments"); } if (!PyList_Check(headers)) { return PyErr_Format(PyExc_TypeError, "http headers must be in a python list"); } size_t h_count = PyList_Size(headers); for (i = 0; i < h_count; i++) { head = PyList_GetItem(headers, i); if (!head) { return NULL; } if (!PyTuple_Check(head)) { return PyErr_Format(PyExc_TypeError, "http header must be defined in a tuple"); } h_key = PyTuple_GetItem(head, 0); if (!h_key) { return PyErr_Format(PyExc_TypeError, "http header must be a 2-item tuple"); } #ifdef PYTHREE if ((self != Py_None && !PyUnicode_Check(h_key)) || (self == Py_None && !PyBytes_Check(h_key))) { #else if (!PyString_Check(h_key)) { #endif return PyErr_Format(PyExc_TypeError, "http header key must be a string"); } h_value = PyTuple_GetItem(head, 1); if (!h_value) { return PyErr_Format(PyExc_TypeError, "http header must be a 2-item tuple"); } #ifdef PYTHREE if ((self != Py_None && !PyUnicode_Check(h_value)) || (self == Py_None && !PyBytes_Check(h_value))) { #else if (!PyString_Check(h_value)) { #endif return PyErr_Format(PyExc_TypeError, "http header value must be a string"); } char *k = NULL; size_t kl = 0; char *v = NULL; size_t vl = 0; #ifdef PYTHREE if (self != Py_None) { zero = PyUnicode_AsASCIIString(h_key); if (!zero) { return PyErr_Format(PyExc_TypeError, "http header must be encodable in latin1"); } k = PyBytes_AsString(zero); kl = PyBytes_Size(zero); } else { k = PyBytes_AsString(h_key); kl = PyBytes_Size(h_key); } #else k = PyString_AsString(h_key); kl = PyString_Size(h_key); #endif #ifdef PYTHREE if (self != Py_None) { zero2 = PyUnicode_AsASCIIString(h_value); if (!zero2) { return PyErr_Format(PyExc_TypeError, "http header must be encodable in latin1"); } v = PyBytes_AsString(zero2); vl = PyBytes_Size(zero2); } else { v = PyBytes_AsString(h_value); vl = PyBytes_Size(h_value); } #else v = PyString_AsString(h_value); vl = PyString_Size(h_value); #endif if (uwsgi_response_add_header(wsgi_req, k, kl, v, vl)) { #ifdef PYTHREE Py_DECREF(zero); Py_DECREF(zero2); #endif return PyErr_Format(PyExc_TypeError, "unable to add header to the response"); } #ifdef PYTHREE Py_DECREF(zero); Py_DECREF(zero2); #endif } if (up.start_response_nodelay) { UWSGI_RELEASE_GIL if (uwsgi_response_write_headers_do(wsgi_req)) { UWSGI_GET_GIL return PyErr_Format(PyExc_IOError, "unable to directly send headers"); } UWSGI_GET_GIL } end: Py_INCREF(up.wsgi_writeout); return up.wsgi_writeout; }