Beispiel #1
0
/**
 * @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;
}
Beispiel #2
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;
}
Beispiel #3
0
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();
}
Beispiel #4
0
/**
 * @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;
}
Beispiel #5
0
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();
}
Beispiel #6
0
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();
}
Beispiel #7
0
void php_sapi_ponion_flush(void *context)  /* {{{ */
{
#endif

	ponion_context_t *ctx = (ponion_context_t*) context;
	if (ctx) {
		onion_response_flush(ctx->res);	
	}
} /* }}} */
Beispiel #8
0
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;
	}
}
Beispiel #9
0
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;
}
Beispiel #10
0
/**
 * @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;
}
Beispiel #11
0
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;
}
Beispiel #12
0
/**
 * @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;
}
Beispiel #13
0
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;
}