Esempio n. 1
0
/******************************************************************************
* subroutine: process_request                                                 *
* purpose:    handle a single request and return responses                    *
* parameters: id        - the index of the client in the pool                 *
*             p         - a pointer of pool struct                            *
*             is_closed - idicator if the transaction is closed               *
* return:     none                                                            *
******************************************************************************/
void process_request(int id, pool *p, int *is_closed)
{
    HTTPContext *context = (HTTPContext *)calloc(1, sizeof(HTTPContext));

    Log("Start processing request. \n");

    // parse request line (get method, uri, version)
    if (parse_requestline(id, p, context, is_closed) < 0) goto Done;

    // check HTTP method (support GET, POST, HEAD now)
    if (strcasecmp(context->method, "GET")  && 
        strcasecmp(context->method, "HEAD") && 
        strcasecmp(context->method, "POST"))
    {
        *is_closed = 1;
        serve_error(p->clientfd[id], "501", "Not Implemented",
                   "The method is not valid or not implemented by the server",
                    *is_closed); 
        goto Done;
    }

    // check HTTP version
    if (strcasecmp(context->version, "HTTP/1.1"))
    {
        *is_closed = 1;
        serve_error(p->clientfd[id], "505", "HTTP Version not supported",
                    "HTTP/1.0 is not supported by Liso server", *is_closed);  
        goto Done;
    }

    // parse uri (get filename and parameters if any)
    parse_uri(context);
   
    // parse request headers 
    if (parse_requestheaders(id, p, context, is_closed) < 0) goto Done;

    // for POST, parse request body
    if (!strcasecmp(context->method, "POST"))
        if (parse_requestbody(id, p, context, is_closed) < 0) goto Done;

    // send response 
    if (!strcasecmp(context->method, "GET"))
        serve_get(p->clientfd[id], context, is_closed); 
    else if (!strcasecmp(context->method, "POST")) 
        serve_post(p->clientfd[id], context, is_closed);
    else if (!strcasecmp(context->method, "HEAD")) 
        serve_head(p->clientfd[id], context, is_closed);

    Done:
    free(context); 
    Log("End of processing request. \n");
}
Esempio n. 2
0
/*
   The PUT method requests that the resource identified by the request
   URI be updated or created with the enclosed representation.  The
   representation format is specified by the media type given in the
   Content-Type Option.

   If a resource exists at the request URI the enclosed representation
   SHOULD be considered a modified version of that resource, and a 2.04
   (Changed) response SHOULD be returned.  If no resource exists then
   the server MAY create a new resource with that URI, resulting in a
   2.01 (Created) response.  If the resource could not be created or
   modified, then an appropriate error response code SHOULD be sent.

   Further restrictions to a PUT can be made by including the If-Match
   (see Section 5.10.9) or If-None-Match (see Section 5.10.10) options
   in the request.

   PUT is not safe, but idempotent.
 */
int serve_put(ec_server_t *srv, ec_rep_t *rep)
{
    /* This routine handles the update of a resource using the PUT method.
     * Creation of a resource via PUT is done by the create() routine. */

    ec_mt_t mt;
    size_t pload_sz;
    uint8_t etag[EC_ETAG_SZ] = { 0 }, *pload;
    ec_res_t *res = ec_rep_get_res(rep);
    size_t bsz = g_ctx.bsz ? g_ctx.bsz : EC_COAP_BLOCK_MAX;
    blockopt_t b1 = { .bnum = 0, .more = false, .bsz = 0 };

    /* Check conditionals:
     * 1) If-None-Match
     * "If the target resource does exist, then the server MUST NOT perform
     *  the requested method.  Instead, the server MUST respond with the 4.12
     *  (Precondition Failed) response code." */
    if (res && ec_request_get_if_none_match(srv) == 0)
    {
        (void) ec_response_set_code(srv, EC_PRECONDITION_FAILED);
        return 0;
    }

    /* 2) If-Match (TODO) */

    /* Get payload and media type (if specified.) */
    pload = ec_request_get_payload(srv, &pload_sz);
    if (pload_sz > bsz)
    {
        (void) ec_response_set_code(srv, EC_REQUEST_ENTITY_TOO_LARGE);
        return 0;
    }

    /* Get media type (if not specified default to text/plain. */
    if (ec_request_get_content_type(srv, &mt))
        mt = EC_MT_TEXT_PLAIN;

    /* Handle Block1 Option.
     * Implementation is stateful: we just check that bnum corresponds to
     * expected and add the new representation atomically. */
    if (ec_request_get_block1(srv, &b1.bnum, &b1.more, &b1.bsz) == 0)
    {
        dbg_err_if (b1.bnum != g_ctx.b1.bnum++);

        if (b1.bsz)
           bsz = U_MIN(bsz, b1.bsz);

        dbg_err_if (ec_response_add_block1(srv, b1.bnum, b1.more, bsz));
    }

    /* First block. */
    if (g_ctx.resbuf == NULL)
        dbg_err_if (u_buf_create(&g_ctx.resbuf));

    /* All blocks are appended to resource buffer. */
    if (pload)
        dbg_err_if (u_buf_append(g_ctx.resbuf, pload, pload_sz));

    /* Last block - now we can process it. */
    if (!b1.more)
    {
        /* Add new representation. */
        dbg_err_if (ec_resource_add_rep(res, u_buf_ptr(g_ctx.resbuf),
                    u_buf_len(g_ctx.resbuf), mt, etag));

        /* Delete old in case media-type matches. */
        if (mt == rep->media_type)
            (void) ec_rep_del(res, rep);

        /* Return Etag of the new representation. */
        (void) ec_response_add_etag(srv, etag, sizeof etag);

        /* Reset state. */
        u_buf_free(g_ctx.resbuf);
        g_ctx.resbuf = NULL;
        g_ctx.b1.bnum = 0;
        g_ctx.b1.more = false;
        g_ctx.b1.bsz = 0;
    }

    (void) ec_response_set_code(srv, EC_CHANGED);
    return 0;
err:
    (void) ec_response_set_code(srv, EC_INTERNAL_SERVER_ERROR);

    /* Reset state. */
    if (g_ctx.resbuf)
        u_buf_free(g_ctx.resbuf);
    g_ctx.resbuf = NULL;
    g_ctx.b1.bnum = 0;
    g_ctx.b1.more = false;
    g_ctx.b1.bsz = 0;

    return -1;
}

/*
 * The POST method requests that the representation enclosed in the
 * request be processed.  The actual function performed by the POST
 * method is determined by the origin server and dependent on the target
 * resource.  It usually results in a new resource being created or the
 * target resource being updated.
 *
 * If a resource has been created on the server, a 2.01 (Created)
 * response that includes the URI of the new resource in a sequence of
 * one or more Location-Path and/or Location-Query Options SHOULD be
 * returned.  If the POST succeeds but does not result in a new resource
 * being created on the server, a 2.04 (Changed) response SHOULD be
 * returned.  If the POST succeeds and results in the target resource
 * being deleted, a 2.02 (Deleted) response SHOULD be returned.
 *
 * POST is neither safe nor idempotent.
 */
/* 
 * Creates a new resource at <request_uri>/<num>, where num is an incremental
 * counter.
 */
int serve_post(ec_server_t *srv)
{
    /* Request vars. */
    ec_mt_t mt;
    ec_res_t *res = NULL;
    bool is_proxy = false;
    uint8_t *pload;
    size_t pload_sz;
    ec_method_mask_t mm = EC_METHOD_MASK_ALL;
    char uri[U_URI_STRMAX];
    char ruri[U_URI_STRMAX];

    /* Block handling. */
    size_t bsz = g_ctx.bsz ? g_ctx.bsz : EC_COAP_BLOCK_MAX;
    blockopt_t b1 = { .bnum = 0, .more = false, .bsz = 0 };

    /* Path tokenisation. */
    size_t nelems, i;
    char **tv = NULL;

    pload = ec_request_get_payload(srv, &pload_sz);
    if (pload_sz > bsz)
    {
        (void) ec_response_set_code(srv, EC_REQUEST_ENTITY_TOO_LARGE);
        return 0;
    }

    /* Get media type (if not specified default to text/plain. */
    if (ec_request_get_content_type(srv, &mt))
        mt = EC_MT_TEXT_PLAIN;
    
    /* Handle Block1 Option.
     * Implementation is stateful: we just check that bnum corresponds to
     * expected and add the new representation atomically. */
    if (ec_request_get_block1(srv, &b1.bnum, &b1.more, &b1.bsz) == 0)
    {
        dbg_err_if (b1.bnum != g_ctx.b1.bnum++);

        if (b1.bsz)
           bsz = U_MIN(bsz, b1.bsz);

        dbg_err_if (ec_response_add_block1(srv, b1.bnum, b1.more, bsz));
    }

    /* First block. */
    if (g_ctx.resbuf == NULL)
        dbg_err_if (u_buf_create(&g_ctx.resbuf));

    /* All blocks are appended to resource buffer. */
    if (pload)
        dbg_err_if (u_buf_append(g_ctx.resbuf, pload, pload_sz));

    /* Last block - now we can process it. */
    if (!b1.more)
    {
        (void) ec_request_get_uri(srv, uri, &is_proxy);

        dbg_err_if (u_snprintf(ruri, sizeof ruri, "%s/%u", uri, g_ctx.resnum));

        u_dbg("creating resource on uri: %s", ruri);

        /* Create resource with all methods allowed. */
        dbg_err_ifm ((res = ec_resource_new(ruri, mm, EC_COAP_DEFAULT_MAX_AGE))
                == NULL, "resource creation failed");

        /* Create new resource representation with the requested media type. */
        /* Each resource only has one representation in this implementation.
         * Use automatic ETag. */
        dbg_err_ifm (ec_resource_add_rep(res, u_buf_ptr(g_ctx.resbuf),
                    u_buf_len(g_ctx.resbuf), mt, NULL),
                "error adding representation for %s", ruri);

        /* Attach resource to FS. */
        dbg_err_ifm (ec_filesys_put_resource(g_ctx.fs, res),
                "adding resource failed");
        res = NULL;

        /* Register the callback that will serve this URI.
         * XXX If we get an error here it's really a bad thing because
         * the resource has been already registered and we go into an
         * inconsistent state. */
        dbg_err_ifm (ec_register_cb(g_ctx.coap, ruri, serve, NULL),
                "registering callback for %s failed", ruri);

        dbg_err_if (u_strtok(ruri, "/", &tv, &nelems));
        dbg_err_if (nelems < 3);

        for (i = 2; i < nelems; i++)  /* Get path only */
            dbg_err_if (ec_response_add_location_path(srv, tv[i]));

        u_strtok_cleanup(tv, nelems);

        /* Reset state. */
        u_buf_free(g_ctx.resbuf);
        g_ctx.resbuf = NULL;
        g_ctx.b1.bnum = 0;
        g_ctx.b1.more = false;
        g_ctx.b1.bsz = 0;

        g_ctx.resnum++;
    }

    (void) ec_response_set_code(srv, EC_CREATED);
    return 0;
err:
    (void) ec_response_set_code(srv, EC_INTERNAL_SERVER_ERROR);
    if (res)
        ec_resource_free(res);
    if (tv)
        u_strtok_cleanup(tv, nelems);

    /* Reset state. */
    if (g_ctx.resbuf)
        u_buf_free(g_ctx.resbuf);
    g_ctx.resbuf = NULL;
    g_ctx.b1.bnum = 0;
    g_ctx.b1.more = false;
    g_ctx.b1.bsz = 0;
    return -1;

#if 0
    /* This routine handles the creation of a resource using the POST method. */
    ec_mt_t mt;
    uint8_t *pload;
    size_t pload_sz;
    ec_res_t *res = NULL;
    bool is_proxy = false;
    ec_method_mask_t mm = EC_METHOD_MASK_ALL;
    char uri[U_URI_STRMAX], *first, *uri_tmp, *uri_res;

    /* Get payload (may be empty/NULL).
     * If it is not empty/NULL check/parse the content */
    pload = ec_request_get_payload(srv, &pload_sz);

    first = strtok((char *) pload, ">");
    do
    {
        uri_tmp = strpbrk(first, "<");

        (void) ec_request_get_uri(srv, uri, &is_proxy);

        uri_res = strcat(uri, ++uri_tmp);

        CHAT("adding resource for: %s", uri_res);

        /* Create resource with all methods allowed. */
        dbg_err_ifm ((res = ec_resource_new(uri_res, mm, 3600)) == NULL,
                "resource creation failed");

        /* Get media type (if not specified default to text/plain. */
        if (ec_request_get_content_type(srv, &mt))
            mt = EC_MT_TEXT_PLAIN;

        /* Create new resource representation with the requested media type. */
        /* Each resource only has one representation in this implementation.
         * Use automatic ETag. */
        dbg_err_ifm (ec_resource_add_rep(res, (const uint8_t *) " ", 1, mt, NULL),
                "error adding representation for %s", uri_res);

        /* Attach resource to FS. */
        dbg_err_ifm (ec_filesys_put_resource(g_ctx.fs, res),
                "adding resource failed");
        res = NULL;

        /* Register the callback that will serve this URI.
         * XXX If we get an error here it's really a bad thing because
         * the resource has been already registered and we go into an
         * inconsistent state. */
        dbg_err_ifm (ec_register_cb(g_ctx.coap, uri_res, serve, NULL),
                "registering callback for %s failed", uri_res);

        first = NULL;
        uri[0] = '\0';
        first = strtok(NULL, ">");
    }
    while (first != NULL);

    /* 2.01 Created */
    (void) ec_response_set_code(srv, EC_CREATED);

    return EC_CBRC_READY;
err:
    if (res)
        ec_resource_free(res);
    return EC_CBRC_ERROR;
#endif
}

#if 0
ec_cbrc_t create(ec_server_t *srv, void *u0, struct timeval *u1, bool u2)
{
    uint8_t *pload;
    size_t pload_sz;
    ec_res_t *res = NULL;
    ec_method_t method;
    bool is_proxy = false;
    char uri[U_URI_STRMAX];
    ec_mt_t mt;
    ec_method_mask_t mm = EC_METHOD_MASK_ALL;

    u_unused_args(u0, u1, u2);

    /* Get the requested URI and method. */
    (void) ec_request_get_uri(srv, uri, &is_proxy);

    switch ((method = ec_server_get_method(srv)))
    {
        case EC_COAP_POST:
            (void) serve_post(srv, NULL);
            return EC_CBRC_READY;
        case EC_COAP_PUT:
            break;
        default:
            (void) ec_response_set_code(srv, EC_NOT_FOUND);
            return EC_CBRC_READY;
    }

    CHAT("adding resource for: %s", uri);

    /* Create resource with all methods allowed. */
    dbg_err_ifm((res = ec_resource_new(uri, mm, 3600)) == NULL,
            "resource creation failed");

    /* Get payload (may be empty/NULL). */
    pload = ec_request_get_payload(srv, &pload_sz);

    /* Get media type (if not specified default to text/plain. */
    if (ec_request_get_content_type(srv, &mt))
        mt = EC_MT_TEXT_PLAIN;

    /* Create new resource representation with the requested media type. */
    /* Each resource only has one representation in this implementation.
     * Use automatic ETag. */
    dbg_err_ifm (ec_resource_add_rep(res, pload, pload_sz, mt, NULL),
            "error adding representation for %s", uri);

    /* Attach resource to FS. */
    dbg_err_ifm (ec_filesys_put_resource(g_ctx.fs, res),
            "adding resource failed");
    res = NULL;

    /* Register the callback that will serve this URI.
     * XXX If we get an error here it's really a bad thing because
     * the resource has been already registered and we go into an
     * inconsistent state. */
    dbg_err_ifm (ec_register_cb(g_ctx.coap, uri, serve, NULL),
            "registering callback for %s failed", uri);

    /* 2.01 Created */
    (void) ec_response_set_code(srv, EC_CREATED);

    return EC_CBRC_READY;
err:
    (void) ec_response_set_code(srv, EC_INTERNAL_SERVER_ERROR);
    if (res)
        ec_resource_free(res);
    return EC_CBRC_ERROR;
}
Esempio n. 3
0
ec_cbrc_t serve(ec_server_t *srv, void *u0, struct timeval *tv, bool resched)
{
    ec_mt_t mta[16];
    size_t mta_sz = sizeof mta / sizeof(ec_mt_t);
    ec_rep_t *rep;
    ec_res_t *res;
    char uri[U_URI_STRMAX];
    bool is_proxy;

    u_unused_args(u0);

    /* Get the requested URI and method. */
    dbg_if (ec_request_get_uri(srv, uri, &is_proxy) == NULL);
    ec_method_t method = ec_server_get_method(srv);

    CHAT("%s %s", ec_method_str(method), uri);

    /* Tell'em to use test/proxy to support Proxy-Uri's. */
    if (is_proxy)
    {
        dbg_if (ec_response_set_code(srv, EC_PROXYING_NOT_SUPPORTED));
        return EC_CBRC_READY;
    }

    /* See if configured for separate responses. */
    if (resched == false && g_ctx.sep.tv_sec)
    {
        *tv = g_ctx.sep;
        u_con("reschedule %s() for %s in %llu seconds",
              __func__, uri, (long long) g_ctx.sep.tv_sec);
        return EC_CBRC_WAIT;
    }

    /* See if it is a query for the /.well-known/core URI. */
    if (!strcasecmp(ec_request_get_uri_path(srv), "/.well-known/core"))
    {
        (void) serve_wkc(srv, method);
        return EC_CBRC_READY;
    }

    /* Get Accept'able media types. */
    dbg_err_if(ec_request_get_acceptable_media_types(srv, mta, &mta_sz));

    /* Try to retrieve a representation that fits client request. */
    rep = ec_filesys_get_suitable_rep(g_ctx.fs, ec_server_get_url(srv), mta, 
            mta_sz, NULL);

    /* If found, craft the response. */
    if (rep)
    {
        dbg_err_if((res = ec_rep_get_res(rep)) == NULL);

        /* Make sure resource supports the requested method.
         * Bypass check for Proxy-Uri requests because of Publish admin
         * operations (will be checked in PUT/DELETE handlers.) */
        if (!ec_request_via_proxy(srv)
                && ec_resource_check_method(res, method))
        {
            (void) ec_response_set_code(srv, EC_METHOD_NOT_ALLOWED);
            return EC_CBRC_READY;
        }

        switch (method)
        {
            case EC_COAP_GET:
                (void) serve_get(srv, rep);
                return EC_CBRC_READY;

            case EC_COAP_DELETE:
                (void) serve_delete(srv, uri);
                return EC_CBRC_READY;

            case EC_COAP_PUT:
                (void) serve_put(srv, rep);
                return EC_CBRC_READY;

            case EC_COAP_POST:
                (void) serve_post(srv);
                return EC_CBRC_READY;

            default:
                ec_response_set_code(srv, EC_NOT_IMPLEMENTED);
                return EC_CBRC_READY;
        }
    }
    else
        (void) ec_response_set_code(srv, EC_NOT_FOUND);

    return EC_CBRC_READY;
err:
    return EC_CBRC_ERROR;
}