/** * @short Writes all the header to the given response * @memberof onion_response_t * @ingroup response * * It writes the headers and depending on the method, return OR_SKIP_CONTENT. this is set when in head mode. Handlers * should react to this return by not trying to write more, but if they try this object will just skip those writtings. * * Explicit calling to this function is not necessary, as as soon as the user calls any write function this will * be performed. * * As soon as the headers are written, any modification on them will be just ignored. * * @returns 0 if should procced to normal data write, or OR_SKIP_CONTENT if should not write content. */ int onion_response_write_headers(onion_response * res) { if (!res->request) { ONION_ERROR ("Bad formed response. Need a request at creation. Will not write headers."); return -1; } res->flags |= OR_HEADER_SENT; // I Set at the begining so I can do normal writing. res->request->flags |= OR_HEADER_SENT; char chunked = 0; if (res->request->flags & OR_HTTP11) { onion_response_printf(res, "HTTP/1.1 %d %s\r\n", res->code, onion_response_code_description(res->code)); //ONION_DEBUG("Response header: HTTP/1.1 %d %s\n",res->code, onion_response_code_description(res->code)); if (!(res->flags & OR_LENGTH_SET) && onion_request_keep_alive(res->request)) { onion_response_write(res, CONNECTION_CHUNK_ENCODING, sizeof(CONNECTION_CHUNK_ENCODING) - 1); chunked = 1; } } else { onion_response_printf(res, "HTTP/1.0 %d %s\r\n", res->code, onion_response_code_description(res->code)); //ONION_DEBUG("Response header: HTTP/1.0 %d %s\n",res->code, onion_response_code_description(res->code)); if (res->flags & OR_LENGTH_SET) // On HTTP/1.0, i need to state it. On 1.1 it is default. onion_response_write(res, CONNECTION_KEEP_ALIVE, sizeof(CONNECTION_KEEP_ALIVE) - 1); } if (!(res->flags & OR_LENGTH_SET) && !chunked && !(res->flags & OR_CONNECTION_UPGRADE)) onion_response_write(res, CONNECTION_CLOSE, sizeof(CONNECTION_CLOSE) - 1); if (res->flags & OR_CONNECTION_UPGRADE) onion_response_write(res, CONNECTION_UPGRADE, sizeof(CONNECTION_UPGRADE) - 1); onion_dict_preorder(res->headers, write_header, res); if (res->request->session_id && (onion_dict_count(res->request->session) > 0)) // I have session with something, tell user onion_response_printf(res, "Set-Cookie: sessionid=%s; httponly; Path=/\n", res->request->session_id); onion_response_write(res, "\r\n", 2); ONION_DEBUG0("Headers written"); res->sent_bytes = -res->buffer_pos; // the header size is not counted here. It will add again so start negative. if ((res->request->flags & OR_METHODS) == OR_HEAD) { onion_response_flush(res); res->flags |= OR_SKIP_CONTENT; return OR_SKIP_CONTENT; } if (chunked) { onion_response_flush(res); res->flags |= OR_CHUNKED; } return 0; }
/** * @short Write some response data. * @memberof onion_response_t * * This is the main write data function. If the headers have not been sent yet, they are now. * * It's internally used also by the write0 and printf versions. * * Also it does some buffering, so data is not sent as written by code, but only in chunks. * These chunks are when the response is finished, or when the internal buffer is full. This * helps performance, and eases the programming on the user side. * * If length is 0, forces the write of pending data. * * @returns The bytes written, normally just length. On error returns OCS_CLOSE_CONNECTION. */ ssize_t onion_response_write(onion_response *res, const char *data, size_t length){ if (res->flags&OR_SKIP_CONTENT){ if (!(res->flags&OR_HEADER_SENT)){ // Automatic header write onion_response_write_headers(res); } ONION_DEBUG("Skipping content as we are in HEAD mode"); return OCS_CLOSE_CONNECTION; } if (length==0){ onion_response_flush(res); return 0; } //ONION_DEBUG0("Write %d bytes [%d total] (%p)", length, res->sent_bytes, res); int l=length; int w=0; while (res->buffer_pos+l>sizeof(res->buffer)){ int wb=sizeof(res->buffer)-res->buffer_pos; memcpy(&res->buffer[res->buffer_pos], data, wb); res->buffer_pos=sizeof(res->buffer); if (onion_response_flush(res)<0) return w; l-=wb; data+=wb; w+=wb; } memcpy(&res->buffer[res->buffer_pos], data, l); res->buffer_pos+=l; w+=l; return w; }
void t02_full_cycle_http10(){ INIT_LOCAL(); onion *server=onion_new(0); onion_add_listen_point(server,NULL,NULL,onion_buffer_listen_point_new()); onion_request *request; char buffer[4096]; memset(buffer,0,sizeof(buffer)); request=onion_request_new(server->listen_points[0]); onion_response *response=onion_response_new(request); onion_response_set_length(response, 30); FAIL_IF_NOT_EQUAL(response->length,30); onion_response_write_headers(response); onion_response_write0(response,"123456789012345678901234567890"); onion_response_flush(response); FAIL_IF_NOT_EQUAL(response->sent_bytes,30); onion_response_free(response); strncpy(buffer,onion_buffer_listen_point_get_buffer_data(request),sizeof(buffer)); onion_request_free(request); onion_free(server); FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.0 200 OK\r\n"); FAIL_IF_NOT_STRSTR(buffer, "Connection: Keep-Alive\r\n"); FAIL_IF_NOT_STRSTR(buffer, "Content-Length: 30\r\n"); FAIL_IF_NOT_STRSTR(buffer, "Server: libonion"); FAIL_IF_NOT_STRSTR(buffer, "coralbits"); FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\n123456789012345678901234567890"); END_LOCAL(); }
/** * @short Tryes to handle the petition with that handler. * @memberof onion_handler_t * * It needs the handler to handle, the request and the response. * * It checks this parser, and siblings. * * @returns If can not, returns OCS_NOT_PROCESSED (0), else the onion_connection_status. (normally OCS_PROCESSED) */ onion_connection_status onion_handler_handle(onion_handler *handler, onion_request *request, onion_response *response){ onion_connection_status res; while (handler){ if (handler->handler){ #ifdef __DEBUG0__ char **bs=backtrace_symbols((void * const *)&handler->handler, 1); ONION_DEBUG0("Calling handler: %s",bs[0]); /* backtrace_symbols is explicitly documented to malloc. We need to call the system free routine, not our onion_low_free ! */ onion_low_free(bs); /* Can't be onion_low_free.... */ #endif res=handler->handler(handler->priv_data, request, response); ONION_DEBUG0("Result: %d",res); if (res){ // write pending data. if (!(response->flags&OR_HEADER_SENT) && response->buffer_pos<sizeof(response->buffer)) onion_response_set_length(response, response->buffer_pos); onion_response_flush(response); if (res==OCS_WEBSOCKET){ if (request->websocket) return onion_websocket_call(request->websocket); else{ ONION_ERROR("Handler did set the OCS_WEBSOCKET, but did not initialize the websocket on this request."); return OCS_INTERNAL_ERROR; } } return res; } } handler=handler->next; } return OCS_NOT_PROCESSED; }
void t06_empty(){ INIT_LOCAL(); onion *server=onion_new(0); onion_add_listen_point(server,NULL,NULL,onion_buffer_listen_point_new()); onion_request *request; char buffer[4096]; memset(buffer,0,sizeof(buffer)); request=onion_request_new(server->listen_points[0]); onion_response *response=onion_response_new(request); // onion_response_write_headers(response); onion_response_flush(response); onion_response_free(response); buffer[sizeof(buffer)-1]=0; strncpy(buffer,onion_buffer_listen_point_get_buffer_data(request),sizeof(buffer)-1); onion_request_free(request); onion_free(server); FAIL_IF_NOT_STRSTR(buffer, "Server:"); FAIL_IF_NOT_STRSTR(buffer, "Content-Type:"); // ONION_DEBUG(buffer); END_LOCAL(); }
void t05_printf(){ INIT_LOCAL(); onion *server=onion_new(0); onion_add_listen_point(server,NULL,NULL,onion_buffer_listen_point_new()); onion_request *request; char buffer[4096]; memset(buffer,0,sizeof(buffer)); request=onion_request_new(server->listen_points[0]); onion_response *response=onion_response_new(request); onion_response_printf(response, "%s %d %p", "Hello world", 123, NULL); onion_response_flush(response); onion_response_free(response); buffer[sizeof(buffer)-1]=0; strncpy(buffer,onion_buffer_listen_point_get_buffer_data(request),sizeof(buffer)-1); onion_request_free(request); onion_free(server); FAIL_IF_NOT_STRSTR(buffer, "Hello world 123 (nil)"); END_LOCAL(); }
void php_sapi_ponion_flush(void *context) /* {{{ */ { #endif ponion_context_t *ctx = (ponion_context_t*) context; if (ctx) { onion_response_flush(ctx->res); } } /* }}} */
void onion_term_destination(j_compress_ptr cinfo) { onion_jpeg_data *d=(onion_jpeg_data*)cinfo->client_data; d->res->buffer_pos = sizeof(d->res->buffer)-cinfo->dest->free_in_buffer; if(onion_response_flush(d->res)<0){ // Flush failed. d->sent = 2; } }
bool print_reqn(int *n, reqres_t * reqres) { ONION_INFO("Writing %d to %p", *n, reqres->res); onion_response_printf(reqres->res, "%d\n", *n); int err = onion_response_flush(reqres->res); if (err < 0) { // Manually close connection free_reqres(reqres); ONION_INFO("Closed connection."); return false; } return true; }
/** * @short Handles a propfind * * @param path the shared path. */ onion_connection_status onion_webdav_propfind(const char *filename, onion_webdav *wd, onion_request* req, onion_response* res){ // Prepare the basepath, necesary for props. char *basepath=NULL; int pathlen=0; const char *current_path=onion_request_get_path(req); const char *fullpath=onion_request_get_fullpath(req); pathlen=(current_path-fullpath); basepath=alloca(pathlen+1); memcpy(basepath, fullpath, pathlen+1); ONION_DEBUG0("Pathbase initial <%s> %d", basepath, pathlen); while(basepath[pathlen]=='/' && pathlen>0) pathlen--; basepath[pathlen+1]=0; ONION_DEBUG0("PROPFIND; pathbase %s", basepath); int depth; { const char *depths=onion_request_get_header(req, "Depth"); if (!depths){ ONION_ERROR("Missing Depth header on webdav request"); return OCS_INTERNAL_ERROR; } if (strcmp(depths,"infinity")==0){ ONION_ERROR("Infinity depth not supported yet."); return OCS_INTERNAL_ERROR; } depth=atoi(depths); } int props=onion_webdav_parse_propfind(onion_request_get_data(req)); ONION_DEBUG("Asking for props %08X, depth %d", props, depth); onion_block *block=onion_webdav_write_propfind(basepath, filename, onion_request_get_path(req), depth, props); if (!block) // No block, resource does not exist return onion_shortcut_response("Not found", HTTP_NOT_FOUND, req, res); ONION_DEBUG0("Printing block %s", onion_block_data(block)); onion_response_set_header(res, "Content-Type", "text/xml; charset=\"utf-8\""); onion_response_set_length(res, onion_block_size(block)); onion_response_set_code(res, HTTP_MULTI_STATUS); onion_response_write_headers(res); onion_response_flush(res); onion_response_write(res, onion_block_data(block), onion_block_size(block)); onion_block_free(block); return OCS_PROCESSED; }
onion_connection_status handler(void *request_list_v, onion_request * req, onion_response * res) { // Some hello message onion_response_printf(res, "Starting poll\n"); onion_response_flush(res); // Add it to the request_list lists. request_list_t *request_list = request_list_v; reqres_t *reqres = onion_low_malloc(sizeof(reqres_t)); reqres->req = req; reqres->res = res; pthread_mutex_lock(&request_list->lock); request_list->reqres = onion_ptr_list_add(request_list->reqres, reqres); pthread_mutex_unlock(&request_list->lock); // Yield thread to the poller, ensures request and response are not freed. return OCS_YIELD; }
/** * @short Frees the memory consumed by this object * @memberof onion_response_t * @ingroup response * * This function returns the close status: OR_KEEP_ALIVE or OR_CLOSE_CONNECTION as needed. * * @returns Whether the connection should be closed or not, or an error status to be handled by server. * @see onion_connection_status */ onion_connection_status onion_response_free(onion_response * res) { // write pending data. if (!(res->flags & OR_HEADER_SENT) && res->buffer_pos < sizeof(res->buffer)) onion_response_set_length(res, res->buffer_pos); if (!(res->flags & OR_HEADER_SENT)) onion_response_write_headers(res); onion_response_flush(res); onion_request *req = res->request; if (res->flags & OR_CHUNKED) { // Set the chunked data end. req->connection.listen_point->write(req, "0\r\n\r\n", 5); } int r = OCS_CLOSE_CONNECTION; // it is a rare ocasion that there is no request, but although unlikely, it may happen if (req) { // keep alive only on HTTP/1.1. ONION_DEBUG0 ("keep alive [req wants] %d && ([skip] %d || [lenght ok] %d==%d || [chunked] %d)", onion_request_keep_alive(req), res->flags & OR_SKIP_CONTENT, res->length, res->sent_bytes, res->flags & OR_CHUNKED); if (onion_request_keep_alive(req) && (res->flags & OR_SKIP_CONTENT || res->length == res->sent_bytes || res->flags & OR_CHUNKED) ) r = OCS_KEEP_ALIVE; if ((onion_log_flags & OF_NOINFO) != OF_NOINFO) // FIXME! This is no proper logging at all. Maybe use a handler. ONION_INFO("[%s] \"%s %s\" %d %d (%s)", onion_request_get_client_description(res->request), onion_request_methods[res->request->flags & OR_METHODS], res->request->fullpath, res->code, res->sent_bytes, (r == OCS_KEEP_ALIVE) ? "Keep-Alive" : "Close connection"); } onion_dict_free(res->headers); onion_low_free(res); return r; }
boolean onion_empty_output_buffer(j_compress_ptr cinfo) { onion_jpeg_data *d=(onion_jpeg_data*)cinfo->client_data; /* * Note: It's not correct to substract free_in_buffer. This value * should be zero, but this depends on the implementation of libjpeg. * It's better to assume that all bytes of the buffer are used. d->res->buffer_pos = sizeof(d->res->buffer)-cinfo->dest->free_in_buffer; */ d->res->buffer_pos = sizeof(d->res->buffer); if(onion_response_flush(d->res)<0){ // Flush failed. d->sent = 2; return FALSE; } cinfo->dest->next_output_byte = (JOCTET *) &d->res->buffer[d->res->buffer_pos]; cinfo->dest->free_in_buffer = sizeof(d->res->buffer)-d->res->buffer_pos; return TRUE; }