Example #1
0
/*
 *   generate a human-readable status report of my threads 
 */
void TadsListenerThread::list_threads(NetString *buf)
{
    /* protect against concurrent access */
    mutex->lock();

    /* scan threads */
    for (TadsServerThread *cur = servers ; cur != 0 ; cur = cur->next_server)
    {
        /* report on this thread */
        StringRef *state = cur->get_state();
        buf->appendf("Thread ID=%d (thread object=%lx): %s<br>",
                     cur->thread_id, (unsigned long)cur, state->get());

        /* done with the state string */
        state->release_ref();
    }

    /* done with concurrent access protection */
    mutex->unlock();
}
Example #2
0
/* 
 *   Process a request from our HTTP client.  The main server loop calls this
 *   when the socket has data ready to read.  
 */
int TadsHttpServerThread::process_request()
{
    StringRef *hdrs = new StringRef(1024);
    TadsHttpRequestHeader *hdr_list = 0, *hdr_tail = 0;
    TadsHttpRequest *req = 0;
    char *verb;
    size_t verb_len;
    char *resource_name;
    size_t res_name_len;
    int ok = FALSE;
    long ofs;
    char *p, *hbody;
    StringRef *body = 0;
    int overflow = FALSE;
    long hbodylen;
    const char *hcl, *hte;             /* content-length, transfer-encoding */

    /* 
     *   Read the header.  We read data into our buffer until we find a
     *   double CR-LF sequence, indicating the end of the header.  
     */
    if ((ofs = read_to_nl(hdrs, 0, 0, 4)) < 0)
        goto done;

    /* the body, if any, starts after the double line break */
    hbody = hdrs->get() + ofs;
    hbodylen = hdrs->getlen() - ofs;

    /* truncate the headers to the CR-LF-CR-LF sequence */
    hdrs->truncate(ofs - 2);

    /*
     *   Parse the main verb in the header - get the method and the resource
     *   ID.  The format is:
     *   
     *.     <space>* VERB <space>+ RESOURCE <space>+ HTTP-VERSION <CRLF>
     */
    p = hdrs->get();
    parse_tok(p, verb, verb_len);
    parse_tok(p, resource_name, res_name_len);

    /* now parse the remaining headers */
    TadsHttpRequestHeader::parse_headers(hdr_list, hdr_tail, FALSE, hdrs, 0);

    /* 
     *   Check to see if there's a message body.  There is if there's a
     *   content-length or transfer-encoding header.
     */
    hcl = hdr_list->find("content-length");
    hte = hdr_list->find("transfer-encoding");
    if (hcl != 0 || hte != 0)
    {
        /* 
         *   There's a content body.  If there's a content-length field,
         *   pre-allocate a chunk of memory, then read the number of bytes
         *   indicated.  If it's a chunked transfer, read it in pieces.  
         */
        if (hcl != 0)
        {
            /* get the length */
            long hclval = atol(hcl);

            /* if it's non-zero, read the content */
            if (hclval != 0)
            {
                /* if this exceeds the size limit, abort */
                if (upload_limit != 0 && hclval > upload_limit)
                {
                    /* set the overflow flag, discard the input, and abort */
                    overflow = TRUE;
                    read(0, 0, hclval, 5000);
                    goto done_with_upload;
                }

                /* allocate the buffer; it's initially empty */
                body = new StringRef(hclval);

                /* copy any portion of the body we've already read */
                if (hbodylen != 0)
                {
                    /* limit this to the declared size */
                    if (hbodylen > hclval)
                        hbodylen = hclval;

                    /* copy the data */
                    body->append(hbody, hbodylen);

                    /* deduct the remaining size */
                    hclval -= hbodylen;
                }

                /* read the body */
                if (hclval != 0
                    && read(body->getend(), hclval, hclval, 5000) < 0)
                {
                    send_simple(S_http_400, "text/html",
                                "Error receiving request message body");
                    goto done;
                }

                /* set the body length */
                body->addlen(hclval);
            }
        }
        else if (stricmp(hte, "chunked") == 0)
        {
            /* set up a string buffer for the content */
            const long initlen = 32000;
            body = new StringRef(hbodylen > initlen ? hbodylen : initlen);

            /* if we've already read some body text, copy it to the buffer */
            if (hbodylen != 0)
                body->append(hbody, hbodylen);
            
            /* keep going until we reach the end marker */
            for (ofs = 0 ; ; )
            {
                /* read to the first newline */
                long nlofs = read_to_nl(body, ofs, 0, 2);
                if (nlofs < 0)
                    goto done;

                /* get the chunk length */
                long chunklen = strtol(body->get() + ofs, 0, 16);

                /* 
                 *   We're done with the chunk length.  Move any read-ahead
                 *   content down in memory so that it directly abuts the
                 *   preceding chunk, so that when we're done we'll have the
                 *   content assembled into one contiguous piece.  
                 */
                long ralen = body->getlen() - nlofs;
                if (ralen > 0)
                    memmove(body->get() + ofs, body->get() + nlofs,
                            body->getlen() - nlofs);

                /* stop at the end of the read-ahead portion */
                body->truncate(ofs + ralen);

                /* if the chunk length is zero, we're done */
                if (chunklen == 0)
                    break;

                /* check if this would overflow our upload size limit */
                if (upload_limit != 0
                    && !overflow
                    && body->getlen() + chunklen > upload_limit)
                {
                    /* flag the overflow, but keep reading the content */
                    overflow = TRUE;
                }

                /* 
                 *   figure the remaining read size for this chunk, after any
                 *   read-ahead portion 
                 */
                long chunkrem = chunklen - ralen;

                /* 
                 *   if we've already overflowed, read and discard the chunk;
                 *   otherwise read it into our buffer 
                 */
                if (chunkrem + 2 <= 0)
                {
                    /* 
                     *   We've already read ahead by enough to cover the next
                     *   chunk and the newline at the end.  Go in and delete
                     *   the newline (or as much of it as exists).  Start by
                     *   getting the offset of the newline: it's just after
                     *   the current chunk, which starts at 'ofs' and runs
                     *   for 'chunklen'.  
                     */
                    nlofs = ofs + chunklen;

                    /* 
                     *   if we haven't yet read the full newline sequence, do
                     *   so now
                     */
                    if (ralen - chunklen < 2)
                    {
                        long nllen = 2 - (ralen - chunklen);
                        body->ensure(nllen);
                        if (read(body->getend(), nllen, nllen, 30000) < 0)
                        {
                            send_simple(S_http_400, "text/html",
                                        "Error receiving request message chunk");
                            goto done;
                        }
                    }

                    /* 
                     *   if there's anything after the newline, move it down
                     *   to overwrite the newline 
                     */
                    if (ralen - chunklen > 2)
                    {
                        /* move the bytes */
                        memmove(body->get() + nlofs,
                                body->get() + nlofs + 2,
                                ralen - chunklen - 2);

                        /* adjust the size for the closed gap */
                        body->truncate(body->getlen() - 2);
                    }

                    /* resume from after the newline */
                    ofs = nlofs;
                }
                else if (overflow)
                {
                    /* overflow - discard the chunk size (plus the CR-LF) */
                    if (read(0, 0, chunkrem+2, 30000) < 0)
                        goto done;

                    /* clear the buffer */
                    body->truncate(0);
                    ofs = 0;
                }
                else
                {
                    /* ensure the buffer is big enough */
                    body->ensure(chunkrem + 2);

                    /* read the chunk, plus the CR-LF that follows */
                    if (read(body->getend(),
                             chunkrem + 2, chunkrem + 2, 30000) < 0)
                    {
                        send_simple(S_http_400, "text/html",
                                    "Error receiving request message chunk");
                        goto done;
                    }

                    /* count the amount we just read in the buffer length */
                    body->addlen(chunkrem + 2);

                    /* verify that the last two bytes are indeed CR-LF */
                    if (memcmp(body->getend() - 2, "\r\n", 2) != 0)
                    {
                        send_simple(S_http_400, "text/html",
                                    "Error in request message chunk");
                        goto done;
                    }

                    /* 
                     *   move past the chunk for the next read, minus the
                     *   CR-LF that follows the chunk - that isn't part of
                     *   the data, but just part of the protocol 
                     */
                    ofs = body->getlen() - 2;
                    body->truncate(ofs);
                }
            }

            /* 
             *   Read to the closing blank line.  We've just passed one
             *   newline, following the '0' end length marker, so we're in
             *   state 2.  We could have either another newline following, or
             *   "trailers" (more headers, following the content body).
             */
            long nlofs = read_to_nl(body, ofs, 2, 4);
            if (nlofs < 0)
                goto done;

            /* 
             *   if we have more than just the closing blank line, we have
             *   trailers - append them to the headers 
             */
            if (nlofs > ofs + 2)
            {
                TadsHttpRequestHeader::parse_headers(
                    hdr_list, hdr_tail, TRUE, body, ofs);
            }

            /* 
             *   the trailers (if any) and closing newline aren't part of the
             *   content - clip them out of the body 
             */
            body->truncate(ofs);
        }
        else
        {
            /* 
             *   Other combinations of these headers are illegal.  Send an
             *   error and abort. 
             */
            send_simple(S_http_400, "text/html",
                        "<html><title>Bad Request</title>"
                        "<h1>Bad Request</h1>"
                        "This server does not accept the specified "
                        "transfer-encoding.");
            goto done;
        }

    done_with_upload:
        /* done with the upload - check for overflow */
        if (overflow)
        {
            /* release the body, if present */
            if (body != 0)
            {
                body->release_ref();
                body = 0;
            }
        }
    }

    /* create a message object for the request */
    req = new TadsHttpRequest(
        this, verb, verb_len, hdrs, hdr_list, body, overflow,
        resource_name, res_name_len,
        listener->get_shutdown_evt());

    /* we've handed over the header list to the request */
    hdr_list = 0;
    
    /* send it to the main thread, and wait for the reply */
    if (queue->send(req, OS_FOREVER))
    {
        /* 
         *   success - the server will already have sent the reply, so we're
         *   done 
         */
    }
    else if (queue->is_quitting())
    {
        /* failed due to server shutdown */
        send_simple(S_http_503, "text/html", S_http_503_quitting_body);
    }
    else
    {
        /* 'send' failed - return an internal server error */
        send_simple(S_http_500, "text/html", S_http_500_body);
    }
    
    /* we're done with the request */
    req->release_ref();

    /* success */
    ok = TRUE;

done:
    /* release the message body buffer */
    if (body != 0)
        body->release_ref();

    /* release the header buffer */
    hdrs->release_ref();

    /* delete the header list */
    if (hdr_list != 0)
        delete hdr_list;

    /* return the success/failure indication */
    return ok;
}