All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.questdb.net.http.IOHttpJob Maven / Gradle / Ivy

There is a newer version: 3.3.3
Show newest version
/*******************************************************************************
 *    ___                  _   ____  ____
 *   / _ \ _   _  ___  ___| |_|  _ \| __ )
 *  | | | | | | |/ _ \/ __| __| | | |  _ \
 *  | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *   \__\_\\__,_|\___||___/\__|____/|____/
 *
 * Copyright (C) 2014-2016 Appsicle
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 *
 ******************************************************************************/

package com.questdb.net.http;

import com.questdb.ex.*;
import com.questdb.log.Log;
import com.questdb.log.LogFactory;
import com.questdb.misc.Net;
import com.questdb.mp.Job;
import com.questdb.mp.RingQueue;
import com.questdb.mp.Sequence;

import java.io.IOException;

public class IOHttpJob implements Job {
    public static final int SO_WRITE_RETRY_COUNT = 1000;
    private final static Log ACCESS = LogFactory.getLog("access");
    private final static Log LOG = LogFactory.getLog(IOHttpJob.class);

    private final RingQueue ioQueue;
    private final Sequence ioSequence;
    private final IODispatcher ioDispatcher;
    private final UrlMatcher urlMatcher;

    IOHttpJob(RingQueue ioQueue, Sequence ioSequence, IODispatcher ioDispatcher, UrlMatcher urlMatcher) {
        this.ioQueue = ioQueue;
        this.ioSequence = ioSequence;
        this.ioDispatcher = ioDispatcher;
        this.urlMatcher = urlMatcher;
    }

    @Override
    public boolean run() {
        long cursor = ioSequence.next();
        if (cursor < 0) {
            return false;
        }

        IOEvent evt = ioQueue.get(cursor);

        final IOContext ioContext = evt.context;
        final int status = evt.channelStatus;
        ioSequence.done(cursor);
        process(ioContext, status);

        return true;
    }

    @Override
    public void setupThread() {
        ioDispatcher.setupThread();
        urlMatcher.setupHandlers();
    }

    private static void logAccess(IOContext context) {
        ACCESS.xinfo().
                $ip(Net.getPeerIP(context.channel.getFd())).
                $(" -").
                $(" -").
                $(" [").
                $ts(System.currentTimeMillis()).
                $("] ").
                $('"').$(context.request.getMethodLine()).$('"').
                $(' ').$(context.getResponseCode()).
                $(' ').$(context.channel.getTotalWrittenAndReset()).
                $();
    }

    private void process(IOContext context, final int channelStatus) {
        final Request r = context.request;
        final SimpleResponse sr = context.simpleResponse();

        int newChannelStatus;

        try {

            boolean log = r.isIncomplete();
            if (channelStatus == ChannelStatus.READ) {
                r.read();
            }

            if (r.getUrl() == null) {
                sr.send(400);
            } else {

                ContextHandler handler = urlMatcher.get(r.getUrl());
                if (handler != null) {
                    switch (channelStatus) {
                        case ChannelStatus.WRITE:
                            context.resume();
                            handler.resume(context);
                            break;
                        case ChannelStatus.READ:
                            if (r.isMultipart()) {
                                if (handler instanceof MultipartListener) {
                                    r.parseMultipart(context, (MultipartListener) handler);
                                    handler.handle(context);
                                } else {
                                    sr.send(400);
                                }
                            } else {
                                if (handler instanceof MultipartListener) {
                                    sr.send(400);
                                } else {
                                    handler.handle(context);
                                }
                            }
                            break;
                        default:
                            LOG.error().$("Unexpected status: ").$(channelStatus).$();
                            break;
                    }
                } else {
                    sr.send(404);
                }

                if (log && !r.isIncomplete()) {
                    logAccess(context);
                }
            }
            context.clear();
            newChannelStatus = ChannelStatus.READ;
        } catch (HeadersTooLargeException ignored) {
            silent(context, 431, null);
            LOG.info().$("Headers too large").$();
            logAccess(context);
            newChannelStatus = ChannelStatus.READ;
        } catch (MalformedHeaderException | DisconnectedChannelException | DisconnectedChannelRuntimeException e) {
            newChannelStatus = ChannelStatus.DISCONNECTED;
        } catch (EndOfChannelException e) {
            newChannelStatus = ChannelStatus.EOF;
        } catch (SlowReadableChannelException e) {
            LOG.debug().$("Slow read").$();
            newChannelStatus = ChannelStatus.READ;
        } catch (SlowWritableChannelException e) {
            LOG.debug().$("Slow write").$();
            newChannelStatus = ChannelStatus.WRITE;
        } catch (Throwable e) {
            context.clear();
            silent(context, 500, e.getMessage());
            newChannelStatus = ChannelStatus.DISCONNECTED;
            LOG.error().$("Internal error: ").$(e).$();
            logAccess(context);
        }
        ioDispatcher.registerChannel(context, newChannelStatus);
    }

    private void silent(IOContext context, int code, CharSequence msg) {
        try {
            context.emergencyResponse().send(code, msg);
        } catch (IOException ignore) {
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy