/** * Copyright 2015 Google Inc. All Rights Reserved. * * 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 com.google.apphosting.vmruntime.jetty9; import static com.google.apphosting.vmruntime.jetty9.VmRuntimeTestBase.logger; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.logging.Level; /** * Minimal implementation of the metadata server running inside each VM. * */ public class TestMetadataServer implements Runnable { private static final String PATH_PREFIX = "/computeMetadata/v1/instance/"; private final int metadataPort; private HashMap<String, String> responses = new HashMap<String, String>(); private boolean run = true; /** * Constructor. * * @param metadataPort The port the server should bind to. */ public TestMetadataServer(int metadataPort) { this.metadataPort = metadataPort; } /** * Adds a new metadata value to the server. * * @param path The path where the value is stored. * @param value The value to return. */ public void addMetadata(String path, String value) { responses.put(PATH_PREFIX + path, value); } /** * Starts a single threaded metadata server. */ @Override public void run() { ServerSocket serverSocket = null; try { logger.fine("TRYING TO Listen for metadata requests at port: " + metadataPort); serverSocket = new ServerSocket(metadataPort); logger.fine("Listening for metadata requests at port: " + metadataPort); while (run) { final Socket clientSocket = serverSocket.accept(); BufferedWriter responseWriter = null; try { BufferedReader requestDataReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); responseWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); String httpGetLine = requestDataReader.readLine(); String[] getLineSplit = httpGetLine.split(" "); if (getLineSplit.length < 2) { responseWriter.write("HTTP/1.0 400 Bad Request\r\n\r\n"); return; } // Seek to the end of the header. if (!verifyHeader(requestDataReader, "Metadata-Flavor: Google")) { responseWriter.write("HTTP/1.0 403 Access Denied\r\n\r\n"); return; } // Check if we have content mapped to the requested path. String requestedPath = getLineSplit[1]; String returnData = responses.get(requestedPath); if (returnData == null) { responseWriter.write("HTTP/1.0 404 Not Found\r\n\r\n"); return; } responseWriter.write("HTTP/1.0 200 OK\r\n"); responseWriter.write("Content-Type: text/plain\r\n\r\n"); responseWriter.write(returnData + "\r\n"); if (requestedPath.endsWith("/STOP")) { run = false; } } finally { if (responseWriter != null) { responseWriter.close(); } clientSocket.close(); } } } catch (IOException e) { logger.log(Level.WARNING, "Exception in TestMetadataServer: ", e); } finally { try { if (serverSocket != null) { serverSocket.close(); logger.fine("CLOSING metadata requests at port: " + metadataPort); } } catch (IOException e) { logger.log(Level.WARNING, "got Exception when closing the server socket.", e); } } } /** * Advance the buffer just past the next empty line, or to the end of the * buffer if no empty line exists. * * @param requestDataReader * @throws IOException */ private boolean verifyHeader(BufferedReader requestData, String header) throws IOException { boolean found = false; String line; do { line = requestData.readLine(); if (line != null && header.equals(line.trim())) { found = true; } } while (line != null && line.trim().length() > 0); return found; } }