summaryrefslogtreecommitdiffstats
path: root/remoting/tools/client_webserver/main.c
blob: f9494b44a9ef3a0c7564f7aa4b5fe430f4579831 (plain)
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Simple webserver that returns empty content with the requested mimetype.
//
// For example:
//   http://localhost:8080/pepper-application/x-chromoting-plugin
// Will return empty content, but with the requested mimetype:
//   Content-Type: pepper-application/x-chromoting-plugin
//
// This is useful for testing the Chromoting plugin while we wait for
// updated mimetype support to be added to Chrome.

#include "build/build_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <netinet/in.h>

#if defined (OS_POSIX)
#include <unistd.h>
#endif //  OS_POSIX

#define PORT 8080

void error(const char *msg) {
  fprintf(stderr, "ERROR - %s\n", msg);
  exit(1);
}

// Read text data from a socket to a buffer.
// Data up to the next \n char is read into the buffer.
void read_text_data(int sock, char *buffer, int buffsize) {
  int num_bytes;
  *buffer = '\0';

  for (num_bytes = 1; num_bytes < buffsize-1; num_bytes++) {
    char ch;

    int num_bytes_read = read(sock, &ch, 1);
    if (num_bytes_read == 1) {
      if (ch == '\n') {
        break;
      }
      *buffer++ = ch;
    } else if (num_bytes_read == 0) {
      break;
    } else {
      error("read_text_data failed");
    }
  }
  *buffer++ = '\0';
}

// Write data from a null-terminated buffer to a socket.
void write_data(int sock, const char *buffer) {
  int num_bytes = strlen(buffer);

  while (num_bytes > 0) {
    int num_bytes_written = write(sock, buffer, num_bytes);
    if (num_bytes_written <= 0) {
      error("write_data failed");
    }
    num_bytes -= num_bytes_written;
    buffer += num_bytes_written;
  }
}

void handle_request(int connection) {
  printf("Handling request...\n");
  char buffer[512];
  buffer[0] = '\0';

  // Read the first line of the request. This will be something like:
  //   GET /index.html HTTP/1.1
  read_text_data(connection, buffer, 512);

  char *saveptr = NULL;
  char *request = strtok_r(buffer, " ", &saveptr);
  char *resource = strtok_r(NULL, " ", &saveptr);
  char *version = strtok_r(NULL, " ", &saveptr);

  char mime_type[512];
  strncpy(mime_type, &resource[1], 511);
  mime_type[511] = '\0';

  if (strcmp(request, "GET")) {
    printf("Unknown request: 'GET %s %s'\n", request, version);
  } else {
    printf("Requesting '%s'\n", mime_type);
  }

  // Keep reading until we encounter a blank line.
  // This will skip over the Host, Connection, User-Agent, ...
  char ignore[512] = "ignore";
  while (strspn(ignore, " \n\r\t") != strlen(ignore)) {
    read_text_data(connection, ignore, sizeof(ignore));
  }

  // At this point, a normal webserver would verify that the requested
  // resource exists and then return it with the appropriate mimetype.

  // However, we return empty content, but with the requested plugin mimetype.
  write_data(connection, "HTTP/1.0 200 OK\r\n");
  write_data(connection, "Content-Type: ");
  // TODO(ajwong): Currently hardcoding the mimetype rather than parsing out of
  // URL to make it easier to invoke the plugin.  Change this back to writing
  // |mime_type| out after we get things cleaned up.
  write_data(connection, "pepper-application/x-chromoting-plugin");
  write_data(connection, "\r\n\r\n");

  // This dummy data is unused, but must be present or else the reader may hang.
  write_data(connection, "Data\n");
}

int main(int argc, char *argv[]) {
  printf("Chromoting client webserver: http://localhost:%d\n", PORT);

  signal(SIGCHLD, SIG_IGN);

  // Create socket.
  int sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    error("Unable to open socket");
  }

  // Initialize socket address struct.
  struct sockaddr_in serv_addr;
  bzero((char*)&serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(PORT);

  // Bind socket.
  if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    error("Unable to bind socket");
  }

  if (listen(sock, 5) < 0) {
    error("Unable to listen to socket");
  }

  while (1) {
    int connection = accept(sock, NULL, NULL);
    if (connection < 0) {
      error("Unable to accept connection");
    }

    pid_t pid = fork();
    if (pid == 0) {
      // Child process.
      if (close(sock) < 0) {
        error("Unable to close socket in child");
      }

      // Handle the request.
      handle_request(connection);

      // Success. Exit to kill child process.
      exit(0);
    } else {
      // Parent process.
      if (close(connection) < 0) {
        error("Unable to close connection in parent");
      }
    }
  }

  return 0;
}