Understanding Virtualization, Docker, HTTP, and Node.js Concepts

  1. Essential Roles of a Virtualization Hypervisor

    • Scheduling CPU time slices for each virtual machine
    • Exposing custom virtualized hardware
    • Allocating system resources across guest operating systems

    Hypervisors are responsible for managing virtual machines by allocating resources and scheduling CPU time. Direct disk access is typically managed through the hypervisor but is not considered a direct role of the hypervisor as it involves more direct interaction with the guest OS.

  2. Relationship of Docker Containers to Docker Images

    • Docker containers execute Docker image contents

    Docker images serve as the blueprint from which containers are created and run, including all dependencies and environment necessary for the application.

  3. Meaning of “Stateless” in HTTP

    • Each client request is independent of previous requests

    HTTP is stateless, meaning that each request from a client to server is treated as an entirely new request without any memory of past interactions.

  4. Comparison Returning False

    • ‘5’ === 5

    In JavaScript, ‘===’ is the strict equality operator that checks for both value and type equality. Since ‘5’ is a string and 5 is a number, this comparison returns false.

  5. Output of Node.js Snippet

    • Analysis required to determine the correct answer
  6. Result of Running Code with Non-existent File

    • The process crashes with an uncaught exception

    Node.js’s asynchronous fs.readFile method’s error handling must occur within the callback function. Using throw inside an asynchronous callback does not lead to the catch block catching it outside the asynchronous operation.

  7. Output of Node.js Snippet (String Prototype Modification)

    • AlphaOmega BetaOmega AlphaOmega

    Modifying the prototype method after the objects have been created affects all instances of the object, as the method resolution occurs at runtime when the method is called.

  8. Output of Node.js Snippet (Calculator Function)

    • Analysis required to determine the correct output, considering the default parameters and rest parameters syntax
  9. Output of Node.js Snippet (Async Functions)

    • Apple Statement1 Banana Statement2

    The order of execution is determined by the timing of the promises and the placement of console logs relative to asynchronous operations.

  10. Effect of Multiple Callback Calls

    • All calls will execute

    In Node.js, if a callback function is called multiple times within a single execution phase, each call will be executed in the order they were called.

Free-Response Questions

  1. Behavior of Node.js Snippet (Large Number Arithmetic)

    • Analysis required to compute the values of A52 and A53 and understand the behavior of large number arithmetic in JavaScript
  2. Outputs of Node.js Code Snippets

    • Analysis required to determine what, if anything, they will output to the console
  3. Encrypted msg Service Implementation

    • This task requires completing the given starter code with specific functionalities for handling users, messages, and encryption. Given the scope, the implementation involves creating an express application, defining API routes, and implementing the required logic for each route using Node.js and the provided classes and methods.

hw2p1 CODE

const http = require(‘http’);
const fs = require(‘fs’);
const url = require(‘url’);

const factorial = (n) => {
  let res = BigInt(1);
  for (let i = 2; i <= n; i++) {
    res *= BigInt(i);
  }
  return res;
};

const numAna = (s) => {
  if (!s || s.trim() === ” || !/^[a-zA-Z]+$/.test(s)) return “0”;
  s = s.toLowerCase();
  const charDict = {};

  for (const char of s) {
    if (!charDict[char]) {
      charDict[char] = 1;
    } else {
      charDict[char]++;
    }
  }

  let res = factorial(s.length);
  for (const count of Object.values(charDict)) {
    res /= factorial(count);
  }

  return res.toString();
};

let reqCount = 0;
let errCount = 0;

const server = http.createServer((req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const path = parsedUrl.pathname;
  const query = parsedUrl.query;

  reqCount++;

  if (req.method === ‘GET’) {
    switch (path) {
      case ‘/ping’:
        res.writeHead(204);
        res.end();
        break;
      case ‘/anagram’:
        const p = query.p;
        if (!p || p.trim() === ” || !/^[a-zA-Z]+$/.test(p)) {
          errCount++;
          res.writeHead(400, { ‘Content-Type’: ‘application/json’ });
          res.end(JSON.stringify({ error: “Parameter ‘p’ is required and must be alphabetical.” }));
        } else {
          const totalAnagrams = numAna(p);
          res.writeHead(200, { ‘Content-Type’: ‘application/json’ });
          res.end(JSON.stringify({ p: p, total: totalAnagrams }));
        }
        break;

      case ‘/secret’:
        fs.exists(‘/tmp/secret.key’, (exists) => {
          if (exists) {
            fs.readFile(‘/tmp/secret.key’, (err, data) => {
              if (err) {
                console.error(err);
                res.writeHead(500);
                res.end();
                return;
              }
              res.writeHead(200, { ‘Content-Type’: ‘text/plain’ });
              res.end(data);
            });
          } else {
            errCount++;
            res.writeHead(404);
            res.end();
          }
        });
        break;

      case ‘/status’:
        res.writeHead(200, { ‘Content-Type’: ‘application/json’ });
        const currentTime = new Date().toISOString().replace(/\.\d{3}/, ”);
        res.end(JSON.stringify({ time: currentTime, req: reqCount, err: errCount }));
        break;

      default:
        errCount++;
        res.writeHead(404);
        res.end();
    }
  } else {
    errCount++;
    res.writeHead(405);
    res.end();
  }
});

server.listen(8088, () => {
  console.log(‘Server running on port 8088’);
});

hw2p2 CODE

const https = require(‘https’);
const querystring = require(‘querystring’);

class SpotifyApi {
  constructor(accessToken) {
    this.accessToken = accessToken;
  }

  static getAccessToken(clientId, clientSecret, callback) {
    const bearer = Buffer.from(`${clientId}:${clientSecret}`).toString(‘base64’);
    const postData = querystring.stringify({ grant_type: ‘client_credentials’ });
    const options = {
      hostname: ‘accounts.spotify.com’,
      path: ‘/api/token’,
      method: ‘POST’,
      headers: {
        Authorization: `Basic ${bearer}`,
        ‘Content-Type’: ‘application/x-www-form-urlencoded’,
        ‘Content-Length’: Buffer.byteLength(postData)
      }
    };

    const req = https.request(options, (res) => {
      let data = ”;
      res.on(‘data’, (chunk) => { data += chunk; });
      res.on(‘end’, () => {
        try {
          const parsed = JSON.parse(data);
          callback(null, parsed.access_token);
        } catch (e) {
          callback(e, null);
        }
      });
    });

    req.on(‘error’, (e) => {
      callback(e, null);
    });

    req.write(postData);
    req.end();
  }

  performRequest(endpoint, callback) {
    const options = {
      hostname: ‘api.spotify.com’,
      path: endpoint,
      method: ‘GET’,
      headers: {
        Authorization: `Bearer ${this.accessToken}`
      }
    };

    const req = https.request(options, (res) => {
      let data = ”;
      res.on(‘data’, (chunk) => { data += chunk; });
      res.on(‘end’, () => {
        if (res.statusCode === 404) {
          callback(new EntityNotFoundError(‘Entity not found’), null);
        } else if (res.statusCode < 200 || res.statusCode >= 300) {
          callback(new ApiError(‘API request failed’), null);
        } else {
          try {
            const parsed = JSON.parse(data);
            callback(null, parsed);
          } catch (e) {
            callback(e, null);
          }
        }
      });
    });

    req.on(‘error’, (e) => {
      callback(e, null);
    });

    req.end();
  }

  getTrack(trackId, callback) {
    const endpoint = `/v1/tracks/${trackId}`;
  
    this.performRequest(endpoint, (err, data) => {
      if (err) {
        callback(err, null);
      } else {
        try {
          const track = {
            albumId: data.album.id,
            artists: data.artists.map(artist => ({
              artistId: artist.id,
              followers: artist.followers,
              genres: artist.genres,
              // Check if ‘images’ exists and has at least one element
              imageUrl: artist.images && artist.images.length > 0 ? artist.images[0].url : null,
              name: artist.name,
              popularity: artist.popularity
            })),
            durationMs: data.duration_ms,
            trackId: data.id,
            name: data.name,
            popularity: data.popularity,
            previewUrl: data.preview_url
          };
          callback(null, track);
        } catch (e) {
          callback(e, null);
        }
      }
    });
  }
  

  searchTracks(query, callback) {
    this.performRequest(`/v1/search?type=track&q=${encodeURIComponent(query)}`, (err, data) => {
      if (err) {
        callback(err, null);
      } else {
        const tracks = data.tracks.items.map(item => ({
          albumId: item.album.id,
          artists: item.artists.map(artist => ({
            artistId: artist.id,
            followers: null, 
            genres: [],
            imageUrl: artist.images && artist.images[0] ? artist.images[0].url : null,
            name: artist.name,
            popularity: null
          })),
          durationMs: item.duration_ms,
          trackId: item.id,
          name: item.name,
          popularity: item.popularity,
          previewUrl: item.preview_url
        }));
        callback(null, tracks);
      }
    });
  }
  
  getArtist(artistId, callback) {
    this.performRequest(`/v1/artists/${artistId}`, (err, data) => {
      if (err) {
        callback(err, null);
      } else {
        const artist = {
          artistId: data.id,
          followers: data.followers.total,
          genres: data.genres,
          imageUrl: data.images && data.images[0] ? data.images[0].url : null,
          name: data.name,
          popularity: data.popularity
        };
        callback(null, artist);
      }
    });
  }
  
  getArtistTopTracks(artistId, marketCode, callback) {
    this.performRequest(`/v1/artists/${artistId}/top-tracks?market=${marketCode}`, (err, data) => {
      if (err) {
        callback(err, null);
      } else {
        const tracks = data.tracks.map(item => ({
          albumId: item.album.id,
          artists: item.artists.map(artist => ({
            artistId: artist.id,
            followers: null,
            genres: [],
            imageUrl: artist.images && artist.images[0] ? artist.images[0].url : null,
            name: artist.name,
            popularity: null
          })),
          durationMs: item.duration_ms,
          trackId: item.id,
          name: item.name,
          popularity: item.popularity,
          previewUrl: item.preview_url
        }));
        callback(null, tracks);
      }
    });
  }
}

class EntityNotFoundError extends Error { };
class ApiError extends Error { };

module.exports = {
  SpotifyApi,
  ApiError,
  EntityNotFoundError
};