static void set_last_chunked_data(write_bucket *bucket) { set2bucket(bucket, "0", 1); set2bucket(bucket, CRLF, 2); set2bucket(bucket, CRLF, 2); }
inline void add_content_length(http_connection *con, char *value, size_t value_len) { data_bucket *bucket = con->bucket; set2bucket(bucket, CONTENT_LENGTH, LEN(CONTENT_LENGTH)); set2bucket(bucket, value, value_len); set2bucket(bucket, CRLF, 2); }
static void set_chunked_data(write_bucket *bucket, char *lendata, size_t lenlen, char *data, size_t datalen) { set2bucket(bucket, lendata, lenlen); set2bucket(bucket, CRLF, 2); set2bucket(bucket, data, datalen); set2bucket(bucket, CRLF, 2); }
static void add_header(write_bucket *bucket, char *key, size_t keylen, char *val, size_t vallen) { set2bucket(bucket, key, keylen); set2bucket(bucket, DELIM, 2); set2bucket(bucket, val, vallen); set2bucket(bucket, CRLF, 2); }
inline void add_kt_xt(http_connection *con, char *value, size_t value_len) { data_bucket *bucket = con->bucket; set2bucket(bucket, X_KT_XT, LEN(X_KT_XT)); set2bucket(bucket, value, value_len); set2bucket(bucket, CRLF, 2); }
inline void add_header(http_connection *con, char *name, size_t name_len, char *value, size_t value_len) { data_bucket *bucket = con->bucket; set2bucket(bucket, name, name_len); set2bucket(bucket, DELIM, 2); set2bucket(bucket, value, value_len); set2bucket(bucket, CRLF, 2); }
inline void set_request_path(http_connection *con, char *method, size_t method_len, char *path, size_t path_len) { data_bucket *bucket = con->bucket; //DEBUG("request URL %s", path); set2bucket(bucket, method, method_len); set2bucket(bucket, path, path_len); set2bucket(bucket, HTTP_11, LEN(HTTP_11)); set2bucket(bucket, CONNECTION_KEEP_ALIVE, LEN(CONNECTION_KEEP_ALIVE)); }
inline void add_body(http_connection *con, char *value, size_t value_len) { //DEBUG("HTTP BODY \n%.*s", value_len, value); data_bucket *bucket = con->bucket; set2bucket(bucket, value, value_len); }
static int processs_write(client_t *client) { VALUE iterator; VALUE v_body; VALUE item; char *buf; size_t buflen; write_bucket *bucket; int ret; // body iterator = client->response_iter; if(iterator != Qnil){ if (TYPE(iterator) != T_ARRAY || RARRAY_LEN(iterator) != 3){ return -1; } v_body = rb_ary_entry(iterator, 2); if(!rb_respond_to(v_body, i_each)){ return -1; } VALUE v_body_str = rb_str_new2(""); rb_block_call(v_body, i_each, 0, NULL, collect_body, v_body_str); if(rb_respond_to(v_body, i_close)) { rb_funcall(v_body, i_close, 0); } buf = StringValuePtr(v_body_str); buflen = RSTRING_LEN(v_body_str); bucket = new_write_bucket(client->fd, 1); set2bucket(bucket, buf, buflen); ret = writev_bucket(bucket); if(ret <= 0){ return ret; } //mark client->write_bytes += buflen; //check write_bytes/content_length if(client->content_length_set){ if(client->content_length <= client->write_bytes){ // all done /* break; */ } } close_response(client); } return 1; }
static void set_first_body_data(client_t *client, char *data, size_t datalen) { write_bucket *bucket = client->bucket; if(data){ if(client->chunked_response){ char *lendata = NULL; Py_ssize_t len = 0; PyObject *chunk_data = get_chunk_data(datalen); //TODO CHECK ERROR PyBytes_AsStringAndSize(chunk_data, &lendata, &len); set_chunked_data(bucket, lendata, len, data, datalen); bucket->chunk_data = chunk_data; }else{ set2bucket(bucket, data, datalen); } } }
inline void set_rest_request_path(http_connection *con, PyObject *dbObj, char *method, size_t method_len, char *path, size_t path_len) { data_bucket *bucket = con->bucket; //DEBUG("request URL %s", path); set2bucket(bucket, method, method_len); if(dbObj){ char *db; Py_ssize_t db_len; PyString_AsStringAndSize(dbObj, &db, &db_len); set2bucket(bucket, "/", 1); set2bucket(bucket, db, db_len); } set2bucket(bucket, "/", 1); set2bucket(bucket, path, path_len); set2bucket(bucket, HTTP_11, LEN(HTTP_11)); set2bucket(bucket, CONNECTION_KEEP_ALIVE, LEN(CONNECTION_KEEP_ALIVE)); }
static int add_status_line(write_bucket *bucket, client_t *client) { PyObject *object; char *value = NULL; Py_ssize_t valuelen; object = client->http_status; //TODO ERROR CHECK if(object){ DEBUG("add_status_line client %p", client); PyBytes_AsStringAndSize(object, &value, &valuelen); //write status code set2bucket(bucket, value, valuelen); add_header(bucket, "Server", 6, SERVER, sizeof(SERVER) -1); //cache_time_update(); add_header(bucket, "Date", 4, (char *)http_time, 29); }else{ DEBUG("missing status_line %p", client); } return 1; }
static int write_headers(client_t *client) { if(client->header_done){ return 1; } write_bucket *bucket; uint32_t i = 0, hlen = 0; VALUE arr; VALUE object; char *name = NULL; ssize_t namelen; char *value = NULL; long valuelen; if(client->headers){ if (TYPE(client->headers) != T_HASH) { return -1; } arr = rb_funcall(client->headers, i_keys, 0); hlen = RARRAY_LEN(arr); } bucket = new_write_bucket(client->fd, ( hlen * 4 * 2) + 32 ); object = client->http_status; if(TYPE(object) != T_STRING){ return -1; } if(object){ value = StringValuePtr(object); valuelen = RSTRING_LEN(object); //write status code set2bucket(bucket, value, valuelen); add_header(bucket, "Server", 6, SERVER, sizeof(SERVER) -1); cache_time_update(); add_header(bucket, "Date", 4, (char *)http_time, 29); if(client->keep_alive == 1){ // Keep-Alive add_header(bucket, "Connection", 10, "Keep-Alive", 10); } else { add_header(bucket, "Connection", 10, "close", 5); } } VALUE object1, object2; //write header if(client->headers){ for(i=0; i < hlen; i++){ object1 = rb_ary_entry(arr, i); Check_Type(object1, T_STRING); if (TYPE(client->headers)!=T_HASH){ goto error; } VALUE tmp = rb_funcall(client->headers, i_key, 1, object1); if (tmp == Qfalse){ goto error; } object2 = rb_hash_aref(client->headers, object1); Check_Type(object2, T_STRING); name = StringValuePtr(object1); namelen = RSTRING_LEN(object1); value = StringValuePtr(object2); valuelen = RSTRING_LEN(object2); if (strchr(name, '\n') != 0 || strchr(value, '\n') != 0) { rb_raise(rb_eArgError, "embedded newline in response header and value"); } if (!strcasecmp(name, "Server") || !strcasecmp(name, "Date")) { continue; } if (!strcasecmp(name, "Content-Length")) { char *v = value; long l = 0; errno = 0; l = strtol(v, &v, 10); if (*v || errno == ERANGE || l < 0) { rb_raise(rb_eArgError, "invalid content length"); goto error; } client->content_length_set = 1; client->content_length = l; } add_header(bucket, name, namelen, value, valuelen); } } set2bucket(bucket, CRLF, 2); client->bucket = bucket; int ret = writev_bucket(bucket); if(ret != 0){ client->header_done = 1; // clear free_write_bucket(bucket); client->bucket = NULL; } return ret; error: /* write_error_log(__FILE__, __LINE__); */ if(bucket){ free_write_bucket(bucket); client->bucket = NULL; } return -1; }
inline void add_header_oneline(http_connection *con, char *value, size_t value_len) { data_bucket *bucket = con->bucket; set2bucket(bucket, value, value_len); }
inline void add_crlf(http_connection *con) { set2bucket(con->bucket, CRLF, 2); }
static response_status process_write(client_t *client) { PyObject *iterator = NULL; PyObject *item, *chunk_data = NULL; char *buf = NULL, *lendata = NULL; Py_ssize_t buflen, len; write_bucket *bucket = NULL; response_status ret; DEBUG("process_write start"); iterator = client->response_iter; if(iterator != NULL){ while((item = PyIter_Next(iterator))){ if(PyBytes_Check(item)){ //TODO CHECK PyBytes_AsStringAndSize(item, &buf, &buflen); //write if(client->chunked_response){ bucket = new_write_bucket(client->fd, 4); if(bucket == NULL){ /* write_error_log(__FILE__, __LINE__); */ call_error_logger(); Py_DECREF(item); return STATUS_ERROR; } lendata = NULL; len = 0; chunk_data = get_chunk_data(buflen); //TODO CHECK ERROR PyBytes_AsStringAndSize(chunk_data, &lendata, &len); set_chunked_data(bucket, lendata, len, buf, buflen); bucket->chunk_data = chunk_data; }else{ bucket = new_write_bucket(client->fd, 1); if(bucket == NULL){ /* write_error_log(__FILE__, __LINE__); */ call_error_logger(); Py_DECREF(item); return STATUS_ERROR; } set2bucket(bucket, buf, buflen); } bucket->temp1 = item; ret = writev_bucket(bucket); if(ret != STATUS_OK){ client->bucket = bucket; /* Py_DECREF(item); */ return ret; } free_write_bucket(bucket); //mark client->write_bytes += buflen; //check write_bytes/content_length if(client->content_length_set){ if(client->content_length <= client->write_bytes){ // all done /* Py_DECREF(item); */ break; } } /* Py_DECREF(item); */ }else{ PyErr_SetString(PyExc_TypeError, "response item must be a byte string"); Py_DECREF(item); if (PyErr_Occurred()){ /* client->bad_request_code = 500; */ client->status_code = 500; /* write_error_log(__FILE__, __LINE__); */ call_error_logger(); return STATUS_ERROR; } } } if(PyErr_Occurred()){ return STATUS_ERROR; } if(client->chunked_response){ DEBUG("write last chunk"); //last packet bucket = new_write_bucket(client->fd, 3); if(bucket == NULL){ /* write_error_log(__FILE__, __LINE__); */ call_error_logger(); return STATUS_ERROR; } set_last_chunked_data(bucket); writev_bucket(bucket); free_write_bucket(bucket); } return close_response(client); } return STATUS_OK; }
static response_status write_headers(client_t *client, char *data, size_t datalen, char is_file) { write_bucket *bucket; uint32_t hlen = 0; PyObject *headers = NULL, *templist = NULL; response_status ret; DEBUG("header write? %d", client->header_done); if(client->header_done){ return STATUS_OK; } //TODO ERROR CHECK headers = PySequence_Fast(client->headers, "header must be list"); hlen = PySequence_Fast_GET_SIZE(headers); bucket = new_write_bucket(client->fd, (hlen * 4) + 42 ); if(bucket == NULL){ goto error; } templist = PyList_New(hlen * 4); bucket->temp1 = templist; if(add_status_line(bucket, client) == -1){ goto error; } //write header if(add_all_headers(bucket, headers, hlen, client) == -1){ //Error goto error; } // check content_length_set if(data && !client->content_length_set && client->http_parser->http_minor == 1){ //Transfer-Encoding chunked add_header(bucket, "Transfer-Encoding", 17, "chunked", 7); client->chunked_response = 1; } if (is_file && !client->content_length_set){ if (set_file_content_length(client, bucket) == -1) { goto error; } } if(client->keep_alive == 1){ //Keep-Alive add_header(bucket, "Connection", 10, "Keep-Alive", 10); }else{ add_header(bucket, "Connection", 10, "close", 5); } set2bucket(bucket, CRLF, 2); //write body client->bucket = bucket; set_first_body_data(client, data, datalen); ret = writev_bucket(bucket); if(ret != STATUS_SUSPEND){ client->header_done = 1; if(ret == STATUS_OK && data){ client->write_bytes += datalen; } // clear free_write_bucket(bucket); client->bucket = NULL; } Py_DECREF(headers); return ret; error: if (PyErr_Occurred()){ /* write_error_log(__FILE__, __LINE__); */ call_error_logger(); } Py_XDECREF(headers); if(bucket){ free_write_bucket(bucket); client->bucket = NULL; } return STATUS_ERROR; }