/* * Copyright (c) 2014 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.werval.server.jre; import java.io.IOException; import java.io.InputStream; import io.werval.api.http.ProtocolVersion; import io.werval.api.http.Request; import io.werval.api.outcomes.Outcome; import io.werval.runtime.outcomes.ChunkedInputOutcome; import io.werval.runtime.outcomes.InputStreamOutcome; import io.werval.runtime.outcomes.SimpleOutcome; import io.werval.spi.ApplicationSPI; import io.werval.spi.dev.DevShellRebuildException; import io.werval.spi.dev.DevShellSPI; import io.werval.spi.server.HttpServerHelper; import io.werval.util.InputStreamByteSource; import io.werval.util.InputStreams; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; /** * com.sun.net HTTP Handler. */ public class WervalHttpHandler implements HttpHandler { private static final int HTTP_BUF_SIZE = 16_384; private final ApplicationSPI app; private final DevShellSPI devSpi; private final HttpServerHelper helper = new HttpServerHelper(); public WervalHttpHandler( ApplicationSPI app, DevShellSPI devSpi ) { this.app = app; this.devSpi = devSpi; } @Override public void handle( HttpExchange exchange ) throws IOException { // New request String requestIdentity = helper.generateNewRequestIdentity(); // In development mode, rebuild application source if needed if( devSpi != null && devSpi.isSourceChanged() ) { try { devSpi.rebuild(); } catch( Exception ex ) { throw new DevShellRebuildException( ex ); } } // Parse request Request request = request( requestIdentity, exchange ); // Handle Request Outcome outcome = app.handleRequest( request ).join(); // Write Outcome writeOutcome( outcome, exchange ); // Done! app.onHttpRequestComplete( request ); } private Request request( String requestIdentity, HttpExchange exchange ) { return app.httpBuilders().newRequestBuilder() .identifiedBy( requestIdentity ) .remoteSocketAddress( exchange.getRemoteAddress().toString() ) .version( ProtocolVersion.valueOf( exchange.getProtocol() ) ) .method( exchange.getRequestMethod() ) .uri( exchange.getRequestURI().toString() ) .headers( exchange.getRequestHeaders() ) .bodyBytes( new InputStreamByteSource( exchange.getRequestBody(), HTTP_BUF_SIZE ) ) .build(); } private void writeOutcome( Outcome outcome, HttpExchange exchange ) throws IOException { // Headers Headers headers = exchange.getResponseHeaders(); headers.putAll( outcome.responseHeader().headers().allValues() ); // Body InputStream bodyStream = null; int chunkSize = HTTP_BUF_SIZE; final long responseLength; if( outcome instanceof ChunkedInputOutcome ) { ChunkedInputOutcome chunked = (ChunkedInputOutcome) outcome; bodyStream = chunked.inputStream(); chunkSize = chunked.chunkSize(); responseLength = 0; } else if( outcome instanceof InputStreamOutcome ) { InputStreamOutcome input = (InputStreamOutcome) outcome; bodyStream = input.bodyInputStream(); responseLength = input.contentLength(); } else if( outcome instanceof SimpleOutcome ) { SimpleOutcome simple = (SimpleOutcome) outcome; bodyStream = simple.body().asStream(); responseLength = 0; } else { responseLength = -1; } // Status and Content-Length exchange.sendResponseHeaders( outcome.responseHeader().status().code(), responseLength ); // Body if any if( bodyStream != null ) { InputStreams.transferTo( bodyStream, exchange.getResponseBody(), chunkSize ); } } }