summaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java
blob: a96cb82285b6b9ce0c054d4d119daa6ade7a8ba2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.http.server.jetty;

import com.yahoo.container.logging.AccessLogEntry;
import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.handler.OverloadException;
import org.eclipse.jetty.server.HttpConnection;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.yahoo.jdisc.http.server.jetty.ConnectorFactory.JDiscServerConnector;

/**
 * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
 * @author bjorncs
 */
@WebServlet(asyncSupported = true, description = "Bridge between Servlet and JDisc APIs")
class JDiscHttpServlet extends HttpServlet {
    public static final String ATTRIBUTE_NAME_ACCESS_LOG_ENTRY
            = JDiscHttpServlet.class.getName() + "_access-log-entry";

    private final static Logger log = Logger.getLogger(JDiscHttpServlet.class.getName());
    private final JDiscContext context;

    public JDiscHttpServlet(JDiscContext context) {
        this.context = context;
    }

    @Override
    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doHead(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doPut(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doDelete(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doOptions(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    @Override
    protected void doTrace(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        dispatchHttpRequest(request, response);
    }

    private static final Set<String> JETTY_UNSUPPORTED_METHODS = new HashSet<>(Arrays.asList(
            "PATCH"));

    /**
     * Override to set connector attribute before the request becomes an upgrade request in the web socket case.
     * (After the upgrade, the HttpConnection is no longer available.)
     */
    @SuppressWarnings("deprecation")
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute(JDiscServerConnector.REQUEST_ATTRIBUTE, getConnector(request));

        Metric.Context metricContext = getMetricContext(request);
        context.metric.add(JettyHttpServer.Metrics.NUM_REQUESTS, 1, metricContext);
        context.metric.add(JettyHttpServer.Metrics.JDISC_HTTP_REQUESTS, 1, metricContext);
        context.metric.add(JettyHttpServer.Metrics.MANHATTAN_NUM_REQUESTS, 1, metricContext);

        if (JETTY_UNSUPPORTED_METHODS.contains(request.getMethod().toUpperCase())) {
            dispatchHttpRequest(request, response);
        } else {
            super.service(request, response);
        }
    }

    static HttpConnection getConnection(HttpServletRequest request) {
        return (HttpConnection)request.getAttribute("org.eclipse.jetty.server.HttpConnection");
    }
    static JDiscServerConnector getConnector(HttpServletRequest request) {
        return (JDiscServerConnector)getConnection(request).getConnector();
    }

    private void dispatchHttpRequest(final HttpServletRequest request,
            final HttpServletResponse response) throws IOException {
        final AccessLogEntry accessLogEntry = new AccessLogEntry();
        AccessLogRequestLog.populateAccessLogEntryFromHttpServletRequest(request, accessLogEntry);
        request.setAttribute(ATTRIBUTE_NAME_ACCESS_LOG_ENTRY, accessLogEntry);
        try {
            switch (request.getDispatcherType()) {
            case REQUEST:
                new HttpRequestDispatch(context,
                        accessLogEntry,
                        getMetricContext(request),
                        request, response).dispatch();
                break;
            default:
                if (log.isLoggable(Level.INFO)) {
                    log.info("Unexpected " + request.getDispatcherType() + "; "
                            + formatAttributes(request));
                }
                break;
            }
        } catch (OverloadException e) {
            // nop
        } catch (RuntimeException e) {
            throw new ExceptionWrapper(e);
        }
    }

    private static Metric.Context getMetricContext(ServletRequest request) {
        return JDiscServerConnector.fromRequest(request)
                .getMetricContext();
    }

    private static String formatAttributes(final HttpServletRequest request) {
        final StringBuilder out = new StringBuilder();
        out.append("attributes = {");
        for (Enumeration<String> names = request.getAttributeNames(); names.hasMoreElements(); ) {
            String name = names.nextElement();
            out.append(" '").append(name).append("' = '").append(request.getAttribute(name)).append("'");
            if (names.hasMoreElements()) {
                out.append(",");
            }
        }
        out.append(" }");
        return out.toString();
    }
}