Servlet has the following properties:
- Triggers upon HTTP request reception
- Has to be developed using dedicated ETSI 102.588 API
- Mapped on one or several URIs (e.g.“/operator/app”)
- Dynamic content generation
- Interception (usage tracking)
GET | Requests the specified resource |
HEAD | Requests meta-information on a resource (no body) |
PUT | Uploads (add or replace) a resource on the server |
POST | Submits data to be processed to the identified resource |
DELETE | Deletes the specified resource on the server |
OPTIONS | Returns the HTTP methods supported for the specified URI (debug purpose) |
TRACE | Echos back the received requests (debug purposes) |
CONNECT method isn't supported by ETSI TS 102.588 standard.
SCWS API has defined in uicc.scws hence you have to import it:
import uicc.scws.*;
Class has to be declared as follows:
class ServletHelloWorld extends Applet implements AppletEvent, ScwsExtension
- Class should extend Applet as it is JavaCard standard applet
- It has to implement ScwsExtension interface to be able to trigger by SCWS
- It has to implement AppletEvent interface to define uninstall() method
- It could implement ToolkitInterface interface if the servlet should be triggered as STK applet
- JavaCard mandatory methods: process(), install()
- ScwsExtension mandatory methods: doDelete(), doGet(), doHead(), doOptions(), doPost(), doPut(), doTrace()
package firstservletpackage; /* * Imported packages */ import javacard.framework.*; import uicc.scws.HttpRequest; import uicc.scws.HttpResponse; import uicc.scws.ScwsConstants; import uicc.scws.ScwsException; import uicc.scws.ScwsExtension; import uicc.scws.ScwsExtensionRegistry; public class FirstServlet extends javacard.framework.Applet implements AppletEvent, ScwsExtension { /** the servlet url */ public final static byte[] url = { '/', 'F', 'S' }; public final static byte[] appId = { 'F', 'i', 'r', 's', 't', 'S', 'e' ,'r', 'v', 'l', 'e', 't' }; public final static byte[] CRLF = { '\r', '\n' }; public static final byte[] SPACE = { ' ' }; /** error status word when install parameter are invalid */ public final static short SW_INVALID_INSTALL_PARAMETER = (short) 0x6F06; /** install tag: should find the http server aid inside */ public final static byte INSTALL_TAG = (byte) 0x71; public final static byte[] METHOD_GET = { 'G', 'E', 'T' }; public final static byte[] TEXT_PLAIN = { 't', 'e', 'x', 't', '/', 'p' ,'l', 'a', 'i', 'n' }; // Temporary operation buffer public byte[] temporaryBuffer; public final static short TEMPORARY_BUFFER_LENGTH = (short) 100; public final static byte[] REQUEST_BODY = { 'R', 'e', 'q', 'u', 'e', 's' ,'t', ' ', 'B', 'o', 'd', 'y', ':' }; public final static byte[] PROTOCOL = { 'P', 'r', 'o', 't', 'o', 'c', 'o' ,'l', ':' }; public final static byte[] CONTENT_TYPE = { 'C', 'o', 'n', 't', 'e', 'n' ,'t', ' ', 'T', 'y', 'p', 'e', ':' }; public final static byte[] HEADER_UA = { 'H', 'e', 'a', 'd', 'e', 'r', ' ' ,'U', 's', 'e', 'r', ' '
, 'A', 'g', 'e', 'n', 't', ':' }; public final static byte[] METHOD = { 'M', 'e', 't', 'h', 'o', 'd', ':' }; public final static byte[] QUERY_STRING = { 'Q', 'u', 'e', 'r', 'y', ' ' ,'S', 't', 'r', 'i', 'n', 'g', ':' }; public final static byte[] REQUEST_URI = { 'R', 'e', 'q', 'u', 'e', 's' ,'t', ' ', 'U', 'R', 'I', ':' }; public final static byte[] TRACE = { 'T', 'R', 'A', 'C', 'E' }; public final static byte[] ACCEPT = { 'A', 'c', 'c', 'e', 'p', 't', ':' }; public final static byte[] ACCEPT_LANG = { 'A', 'c', 'c', 'e', 'p', 't' ,'-', 'L', 'a', 'n', 'g', 'u', 'a', 'g', 'e', ':' }; public final static byte[] CONNECTION = { 'C', 'o', 'n', 'n', 'e', 'c' ,'t', 'i', 'o', 'n', ':' }; public final static byte[] HOST = { 'H', 'o', 's', 't', ':' }; public final static byte[] REFERER = { 'R', 'e', 'f', 'e', 'r', 'e', 'r', ':' }; public final static byte[] USER_AGENT = { 'U', 's', 'e', 'r', '-', 'A' ,'g', 'e', 'n', 't', ':' }; public final static byte[] ACCEPT_ENCODING = { 'A', 'c', 'c', 'e', 'p', 't'
, '-', 'E', 'n', 'c', 'o', 'd'
, 'i', 'n', 'g', ':' }; public final static byte[] ALLOW = { 'A', 'l', 'l', 'o', 'w' }; public final static byte[] ALLOW_OPTIONS = { 'G', 'E', 'T', ','
, 'T', 'R', 'A', 'C', 'E' , ',', 'O', 'P', 'T', 'I', 'O', 'N', 'S' }; public final static byte[] HTTP_10 = { 'H', 'T', 'T', 'P', ':'
, '/', '1', '.', '0' }; public final static byte[] HTTP_11 = { 'H', 'T', 'T', 'P', ':'
, '/', '1', '.', '1' }; public final static byte[] HELLO = { 'H', 'e', 'l', 'l', 'o' }; /** * Constructor of servlet */ public FirstServlet(byte[] buffer, short offset, byte length) { // First LV is instance AID short aid = offset; offset += buffer[offset] + (byte) 1; // Second LV is Privilege offset += buffer[offset] + (byte) 1; // Third LV is specific install parameter (extract from TAG C9) offset++; // skip C9 Length // Register the new applet instance to the JCRE register(buffer, (short) (aid + (short) 1), buffer[aid]); // Register application id,there is corresponding appId in the Run/Debug // configuration for URL Mapping // if you update this appId in current code,you may need update // corresponding value there for URL Mapping ScwsExtensionRegistry.register(this, appId, (short) 0, (short) appId.length); try { // Create a temporary buffer for read/write temporaryBuffer = JCSystem.makeTransientByteArray( TEMPORARY_BUFFER_LENGTH, JCSystem.CLEAR_ON_RESET); } catch (SystemException se) { // create buffer in persistent memory as not enough transient is // available temporaryBuffer = new byte[TEMPORARY_BUFFER_LENGTH]; } } /** * Method called by the JCRE at the installation of the applet * * @param bArray * the byte array containing the AID bytes * @param bOffset * the start of AID bytes in bArray * @param bLength * the length of the AID bytes in bArray */ public static void install(byte[] bArray, short sOffset, byte bLength) throws ISOException { new FirstServlet(bArray, sOffset, bLength); } /** * Method called by the JCRE, uninstall the applet */ public void uninstall() { ScwsExtensionRegistry.deregister(this); } /** * Method called by the JCRE, once selected * * @param apdu * the incoming APDU object */ public void process(APDU apdu) throws ISOException { // ignore the applet select command dispached to the process if (selectingApplet()) { return; } // actually no IO methods needed for the server. ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } /** * Called by the SCWS to allow a servlet to handle a DELETE request. * <p> * If the HTTP DELETE request is incorrectly formatted, doDelete returns an * HTTP "Bad Request" message. * </p> * * @param req * - the HttpRequest object that contains the request the client * made of the servlet * @param resp * - the HttpResponse object that contains the response the * servlet returns to the client * @throws ScwsException * - if the request for the DELETE cannot be handled */ public void doDelete(HttpRequest req, HttpResponse resp) throws ScwsException { } /** * Called by SCWS to allow a servlet to handle a GET request. * <p> * When using HTTP 1.1 chunked encoding (which means that the response has a * Transfer-Encoding header), do not set the Content-Length header. * </p> * If the HTTP POST request is incorrectly formatted, doPost returns an HTTP * "Bad Request" message. * * @param req * - an HttpRequest object that contains the request the client * has made of the servlet * @param resp * - an HttpResponse object that contains the response the * servlet sends to the client * @throws ScwsException * - if the request for the GET could not be handled */ public void doGet(HttpRequest req, HttpResponse resp) throws ScwsException { // TODO Replace your doGet method below // ---------------------------------------------------------------------- // return on response body: // status: 200 (OK) // Content Type: text/html // Protocol: HTTP/1.1 // Method: GET // Header User Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) // Content Type: text/html // ... try { short httpVersion = req.getRequestHttpVersion(); // set header resp.writeStatusCode(ScwsConstants.SC_OK); resp.setContentType(ScwsConstants.CONTENT_TYPE_TEXT_HTML); resp.enableChunkMode(); // write body resp.appendContent(HELLO, (short) 0, (short) HELLO.length); } catch (Exception e) { resp.writeStatusCode(ScwsConstants.SC_BAD_REQUEST); } resp.flush(); } /** * Receives an HTTP HEAD request from the protected service method and * handles the request. * <p> * The client sends a HEAD request when it wants to see only the headers of * a response, such as <tt>Content-Type</tt> or <tt>Content-Length</tt>.<br> * <p> * The HTTP HEAD method counts the output bytes in the response to set the * Content-Length header accurately. * <p> * If the HTTP HEAD request is incorrectly formatted, doHead returns an HTTP * "Bad Request" message. * * @param req * - the request object that is passed to the servlet * @param resp * - the response object that the servlet uses to return the * headers to the clien * @throws ScwsException * - if the request for the HEAD could not be handled */ public void doHead(HttpRequest req, HttpResponse resp) throws ScwsException { } /** * The OPTIONS method represents a request for information about the * communication options available on the request/response chain identified * by the Servlet. * <p> * Called by the SCWS to allow a servlet to handle a OPTIONS request. <br> * The OPTIONS request determines which HTTP methods the server supports and * returns an appropriate header.<br> * For example, if a servlet overrides doGet, this method returns the * following header: * * <pre> * Allow: <tt>GET</tt>, <tt>HEAD</tt>, <tt>OPTIONS</tt> * </pre> * * </p> * * @param req * - the HttpRequest object that contains the request the client * made of the servlet * @param resp * - the HttpResponse object that contains the response the * servlet returns to the client * @throws ScwsException * - if the request for the OPTIONS cannot be handled */ public void doOptions(HttpRequest req, HttpResponse resp) throws ScwsException { resp.appendHeaderVariable(ALLOW, (short) 0, (short) ALLOW.length, ALLOW_OPTIONS, (short) 0, (short) ALLOW_OPTIONS.length); resp.flush(); } /** * Called by the SCWS to allow the servlet to handle a POST request. * <p> * When using HTTP 1.1 chunked encoding (which means that the response has a * Transfer-Encoding header), do not set the Content-Length header. * </p> * If the HTTP POST request is incorrectly formatted, doPost returns an HTTP * "Bad Request" message. * * @param req * - an HttpRequest object that contains the request the client * has made of the servlet * @param resp * - an HttpResponse object that contains the response the * servlet sends to the client * @throws ScwsException * - if the request for the POST could not be handled */ public void doPost(HttpRequest req, HttpResponse resp) throws ScwsException { } /** * Called by the SCWS (via the service method) to allow a servlet to handle * a PUT request. * <p> * If the HTTP PUT request is incorrectly formatted, doPut returns an HTTP * "Bad Request" message. * * @param req * - the HttpRequest object that contains the request the client * made of the servlet * @param resp * - the HttpResponse object that contains the response the * servlet returns to the client * @throws HttpCardServletException * - if the request for the PUT cannot be handled * @throws java.lang.Exception */ public void doPut(HttpRequest req, HttpResponse resp) throws ScwsException { } /** * Called by the server (via the service method) to allow a servlet to * handle a TRACE request. * <p> * The TRACE method is used to invoke a remote, application-layer loop-back * of the request message. * </p> * * @param req * - the HttpRequest object that contains the request the client * made of the servlet * @param resp * - the HttpResponse object that contains the response the * servlet returns to the client * @throws ScwsException * - if the request for the TRACE cannot be handled */ public void doTrace(HttpRequest req, HttpResponse resp) throws ScwsException { // TODO Auto-generated method stub // -------------------------- /* * A TRACE returns the headers sent with the TRACE request to the * client, so that they can be used in debugging. */ try { // set header resp.writeStatusCode(ScwsConstants.SC_OK); resp.setContentType(ScwsConstants.CONTENT_TYPE_TEXT_PLAIN); resp.enableChunkMode(); // TRACE /MyServlet1 HTTP/1.1 resp.appendContent(TRACE, (short) 0, (short) TRACE.length); resp.appendContent(SPACE, (short) 0, (short) SPACE.length); getAndOutputContent(req, resp, ScwsConstants.URI_PATH_TAG); resp.appendContent(SPACE, (short) 0, (short) SPACE.length); short httpVersion = req.getRequestHttpVersion(); if (httpVersion == ScwsConstants.HTTP_PROTOCOL_VERSION_10) resp.appendContent(HTTP_10, (short) 0, (short) HTTP_10.length); else resp.appendContent(HTTP_11, (short) 0, (short) HTTP_11.length); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); // ex: Accept: */* resp.appendContent(ACCEPT, (short) 0, (short) ACCEPT.length); getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); // ex: Accept-Language: en-us resp.appendContent(ACCEPT_LANG, (short) 0, (short) ACCEPT_LANG.length); getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT_LANGUAGE); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); // ex: Host: localhost resp.appendContent(HOST, (short) 0, (short) HOST.length); getAndOutputContent(req, resp, ScwsConstants.HEADER_HOST); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); // ex: User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT // 5.0) resp .appendContent(USER_AGENT, (short) 0, (short) USER_AGENT.length); getAndOutputContent(req, resp, ScwsConstants.HEADER_USER_AGENT); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); // ex: Accept-Encoding: gzip, deflate resp.appendContent(ACCEPT_ENCODING, (short) 0, (short) ACCEPT_ENCODING.length); getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT_ENCODING); resp.appendContent(CRLF, (short) 0, (short) CRLF.length); } catch (Exception e) { resp.writeStatusCode(ScwsConstants.SC_NOT_IMPLEMENTED); } resp.flush(); } private void getAndOutputContent(HttpRequest req, HttpResponse resp, short keyword) { short contentLgth = req.findAndCopyKeywordValue(keyword, temporaryBuffer, (short) 1, (short) (temporaryBuffer.length - 1)); if (contentLgth == ScwsConstants.KEYWORD_NOT_FOUND) contentLgth++; // Query string may not exist, so set it 0 if did temporaryBuffer[0] = (byte) (contentLgth); resp.appendContent(temporaryBuffer, (short) 1, (short) temporaryBuffer[0]); clearBuffer(); // Clear buffer for reuse } private void clearBuffer() { for (short i = 0; i < temporaryBuffer.length; i++) { temporaryBuffer[i] = 0; } } private short getLength(byte[] data) { for (short i = 0; i < data.length; i++) { if (data[i] == 0) { return i; } } return 0; } }
No comments:
Post a Comment